I'm using this script:
http://www.webvamp.co.uk/blog/coding/creating-one-time-download-links/
to allow users download files (one time). Everything works fine with small files. Now i'm trying to do the same but with larger file 1.2 GB. Instead of forcing user to download file, script show off the relative patch to the file! Is there any way to modify the script or its a fault of the server configuration?
Thanks for help!
Looking for the code i think it fails on large files due to memory limitation. Script reads the whole file in memory via file_get_contents() before sending it. I suspect, >1Gb files will cause the problems with memory.
Try to replace following lines in download.php script:
//get the file content
$strFile = file_get_contents($strDownload);
//set the headers to force a download
header("Content-type: application/force-download");
header("Content-Disposition: attachment;
filename=\"".str_replace(" ", "_", $arrCheck['file'])."\"");
//echo the file to the user
echo $strFile;
to:
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($strDownload));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($strDownload));
ob_clean();
flush();
readfile($strDownload);
This may be help.
Note from manual: readfile() will not present any memory issues, even when sending large files, on its own
Related
I want to "pipe" a file through PHP.
My PHP script calls a bash script through "shell_exec". The bash script downloads a file and prints its data. The PHP script then continues to send the file as response:
if (!file_exists("./$filename")) shell_exec("./get-file $file_id \"$filename\"");
if (file_exists($filename)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($filename) . '"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($filename));
readfile($filename);
unlink($filename);
exit;
}
This works okay. But is there some way to "pipe" the file while it's being downloaded? Let's say the file is 500MB, instead of waiting for the file to download in the server, and then serve it to the client, is there some way to send the content as it's being downloaded? The reason is obvious; I want to minimize the total time it takes for the whole process to complete. If what I ask is possible, it could bring total time to half. Thank you.
Edit: I found this but the solution is based on curl. In my case, the file is being downloaded by external script. So I want to run a loop in which the PHP script checks if new bytes are written in the file, and proceed to sending them to the client, without closing the connection. Note that the file is not text-based, it's an audio file.
Edit: Now I'm closer: I modified the external script to output the file in stdout, effectively feeding its data to shell_exec. Now, how can I send data before shell_exec has finished? I guess I'll have to use something like ob_flush() but some help would be appreciated. Thanks again
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($filename) . '"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
// This sends the data AFTER the exec finishes. I want to send them as they arrive, real-time
echo shell_exec("get-file $file_id");
die();
OK I found the answer in other posts.
I just use passthru() instead of shell_exec() and it seems to be working great.
Thank everybody! <3
For some reason, our webserver is not responding while it's serving large files.
We use the windows platform, because we need to remotely call Win32 applications in order to generate the file that is to be served. This file is served through PHP's function: fpassthru, using this code:
if (file_exists($file)) {
$handle = #fopen($file, "rb");
header('Content-Description: File Transfer');
header('Content-Type: video/mp4');
if($stream==0){
header('Content-Disposition: attachment; filename='.basename($filename.".mp4"));
}
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_end_clean();
fpassthru($handle);
exit;
}
These files are often over 1GB in size and takes a while to transfer, but during this time, the webserver will not serve any pages. My firefox indicates it's 'connecting' but nothing else. Note that somebody else is transferring this file, not me, so different IP, different session.
Any clue where to look? Obviously, it's intolerable to have to wait 5 minutes for a website.
Thanks in advance!
This is commonly caused when you do not close the session before you begin sending the file data. This is because the session cache file can only be opened by one PHP process at a time, therefore the download is effectively blocking all other PHP processes at session_start().
The solution is to call session_write_close() to commit the session data to disk and close the file handle before you start outputting the file data.
I have fried my brain all day on this. Researching SO until my eyes are bleary...I need to know: How do I access files placed outside the site root?
Background: Apache 2.0 dedicated server running Linux.
Code: PHP and MySQL
Reason: I want the files to be secured against typing in the file path and filename into a browser.
This can't be that difficult...but my splitting head says otherwise. Any help would be absolutely appreciated.
Have a look at the answers to this question, which seem to be doing more or less the same thing.
A quick summary: readfile() or file_get_contents() are what you're after. This example comes from the readfile() page:
<?php
$file = 'monkey.gif';
if (file_exists($file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($file));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
exit;
}
?>
I don't recommend allowing the $file variable to be set using user input! Think about where the filenames are coming from before arbitrarily returning files in the response.
are you trying to access files outside of site root? Then you can look at this link in stackoverflow.
And this is the official doc in Apache.
Otherwise you don't have to do special handling to prevent others from accessing files outside site root.
I have this code:
$file = $tempDir . "/download.zip";
// there's some omitted code here that creates the file that's to be downloaded
if(file_exists($file) && is_readable($file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename='.basename($file));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
}else{
return "Error: Failed to retrieve file.";
}
The code that generates the file works fine, and after hitting the button for downloading, I see it appear in its appropriate place, at 1 KB. The file is also usable. When I download, it even says it's "973 bytes". When the file actually downloads though, it's suddenly 9.1 KB, and completely corrupted. Why?
does the code omitted run a background program? If you run a command line program (exec etc) to create the zip file you may be serving an incomplete file.
upload a zip file that you know works, remove the code to create a zip and see if that is served correctly. if it isn't let me know and i'll take a look.
is this windows or linux?
also the size of the file may vary slightly. windows adds meta data, created,added,modified etc that can effect the file size
I'm trying to write a php script that will check parameters passed in before it initiates a download to the client. I've started by attempting to just initiate a download:
<?php
$file = '/tticon.jpg';
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header("Content-Disposition: attachment; filename=\"".basename($file)."\"");
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
exit;
?>
When I navigate to this script it shows a blank page and nothing happens. How do I initiate a download?
Try using application/force-download as Content-Type. If you want to show the image in the brwoser you can use image/jpg.
header("Content-type: application/force-download");
Since the file's extension is ".jpg" I assume it's mime is image/jpeg.
Replace this line:
header('Content-Type: application/octet-stream');
with this:
header('Content-Type: image/jpeg');
If you really want to force the browser to download the file (which I find very unlikely, anyway):
Replace the line with this:
header("Content-Type: application/force-download");
Your $file looks suspect. Do you really have a tticon.jpg in the root directory of your server? Remember that PHP's file-functions operate on the FILESYSTEM of the server, not the WEB directories that Apache presents. PHP will not magically pre-pend your site's document root to that path. It's literally going to be looking in the root directory of the server's file system, NOT in the document root of your site.