I have written a code that offers visitors to download a csv file which is of 1.1M in size. If one visits this script, download is interrupted around 30-40K (as shown below in wget output) while if he downloads it via a direct link like http://domain.com/events.csv it works just fine. I believe this has something to do with php configuration values on the server but i have played almost with all values[relevant and non-relevant] such as
post_max_size [upto 90M]
max_file_upload [upto 90M]
max_execution_time [0 and upto 600]
max_input_time [0 and upto 600]
memory_limit [upto 1024M]
Following contains my code:
<?php
$realpath="/home/user/public_html/events.csv";
$size = intval(sprintf("%u", filesize($realpath)));
#ini_set('zlib.output_compression', 0);
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");;
header("Content-Disposition: attachment;filename=events.xls");
header("Content-Transfer-Encoding: binary ");
header("Pragma: public");
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: pre-check=0, post-check=0, max-age=0');
header ("Pragma: no-cache");
header("Expires: 0");
header("Content-Description: File Transfer");
header("Content-Type: text/csv");
header("Content-Length: ". $size);
// also tried with having a flush(); here
// also tried with file_get_contents();
//also tried with wrapping the file_get_contents() or readfile() call inside ob_start() and ob_flush()+ob_clean()
readfile($realpath);
exit;
?>
Here is the wget output
wget "http://domain.com/test.php"
--2011-06-26 19:47:55-- http://domain.com/test.php
Resolving domain.com... 69.117.110.115
Connecting to domain.com|69.117.110.115|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1139844 (1.1M) [text/csv]
Saving to: `test.php'
3% [===> ] 36,373 --.-K/s in 11s
2011-06-26 19:48:11 (3.29 KB/s) - Connection closed at byte 36373. Retrying.
--2011-06-26 19:48:12-- (try: 2) http://domain.com/test.php
Connecting to domain.com|69.117.110.115|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1139844 (1.1M) [text/csv]
Saving to: `test.php'
3% [===> ] 40,469 --.-K/s in 11s
2011-06-26 19:48:24 (3.66 KB/s) - Connection closed at byte 40469. Retrying.
If i remove the header() required to offer a download and just echo the contents, then Chrome shows that the test.php was around 1.09M plus some more request, adds upto 1.1M[even in this case wget of test.php shows same behaviour as above], while firefox firebug shows that request was between 140K-300K[still not displaying all contents.
Check http://www.php.net/manual/en/function.readfile.php and search for: large. Copy-paste the code :)
Related
Situation: we store files on Amazon S3 and write the infos into our database. Now the user can download the files by a download link like www.myserver.de/downloadFile?id=1234
This works quite well, but we get several "Maximum execution time of 800 seconds exceeded" errors.
Our current code for the download:
header("Pragma: public");
header("Expires: -1");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
if($mimeType)
{
header("Content-Type: ".$mimeType);
}
else
{
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=".$filename.";");
}
header("Content-length: ".$filesize);
$s3Client = new s3Client();
//download the file from s3 to local server
$s3Client->getFile($folder, $filename, $tempFolder.$filename);
$filehandle = fopen($tempFolder.$filename, "rb");
while(!feof($filehandle))
{
print(#fread($filehandle, 1024*8));
ob_flush();
flush();
if(connection_status() != 0)
{
#fclose($filehandle);
unlink($tempFolder.$filename);
exit;
}
}
#fclose($filehandle);
unlink($tempFolder.$filename);
exit;
The max execution time error happens even with very small files (just some KB). It looks like there is a problem in while loop and it wont be terminated?
The strange thing is: the download works anyway?
When we get the timeout error in our log, we try the download ourselves and it works perfectly.
The source for this way of downloading files is: http://www.media-division.com/the-right-way-to-handle-file-downloads-in-php/
I'm creating a protected file download system for an e-commerce website. I am using PHP to authenticate that a user is logged in and owns the product before it is downloaded. I am using the example provided by the PHP.net manual but have run into an error serving both PDF and PNG files.
It has worked at one point, during development. Now that we've gone live, it seems to have broke... Fortunately the website is not running full-force at the moment so it's not a huge issue right now.
What is happening:
You visit the download URL. You are verified as an owner of the product, and the download starts. Everything appears to be normal, reviewing the headers everything looks OK. The header states the content length is "229542" (224.16kb), which looks OK.
The problem:
When the download completes, the file size is only 222 bytes. There are no errors displayed, and no PHP errors/warnings/notices are being sent in the file or browser. It's as if PHP is being terminated, but without any warnings. Even if I turn debugging on, I don't see any warnings.
The source file looks like this (in Notepad++, it's a PDF so it's binary)
When downloaded, it looks like this (Only 7 lines, compared to 2554)
My guess:
I am fairly certain the issue is header related. Maybe caused by a plugin? This shows up in Chrome's networking console:
Response Headers:
Below are the response headers. The "Connection: close" concerns me. I am not sending that header. I'm guessing it is sent by the exit command.
Access-Control-Allow-Origin:*
Cache-Control:must-revalidate, post-check=0, pre-check=0
Connection:close
Content-Description:File Transfer
Content-Disposition:attachment; filename="workbook.pdf"
Content-Length:229542
Content-Transfer-Encoding:binary
Content-Type:application/pdf
Date:Tue, 03 Sep 2013 21:16:14 GMT
Expires:0
Pragma:public
Server:Apache
X-Powered-By:PleskLin
I have also tried:
Turning on PHP debugging to get some sort of error/warning, but I don't get anything useful in browser or in the downloaded file source.
Outputting to the browser, rather than streaming. The file is still cut short, I would expect a PDF would try to display in the browser's PDF reader - but it doesn't even try. Just a white page with a bunch of unknown symbols.
Using +rb read mode, using fopen( $file['file'], 'rb' ); echo fread( $fh, filesize( $file['file'] ) ); fclose( $fh );
Using ignore_user_abort(true) and set_time_limit(0) to no effect (also tried set_time_limit(10000) just for in case, still nothing)
Sending a .txt file. This worked. I tried with a simple 5-byte text file, and again with an 86.3kb file. Both appear to have come out the same as they went in. So it must be related to binary files...
/* --- This is what the $file variable looks like:
$file = array(
[file] => /var/www/vhosts/my-website/uploads/workbook.pdf
[type] => application/pdf
[filesize] => 229542
[filename] => workbook.pdf
[upload_date] => 1377278303
);
*/
// Stream the file to the user
if (file_exists($file['file'])) {
header_remove(); // Remove any previously set header
header('Content-Description: File Transfer');
header('Content-Type: ' . $file['type']);
header('Content-Disposition: attachment; filename="'.$filename.'"');
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);
ob_clean();
flush();
readfile($file['file']);
exit;
}
I know this is an older thread but I have the solution for everyone here.
You have a script somewhere in your application that's compressing spaces or other characters.
ob_start("sanitize_output");
That was the problem in one scenario I noticed. If you turn off the buffer reduction script (called "sanitize_output" above) then your .pdf will show up normally. That extra 2kb's you noticed that were trimmed was probably from spacing or cutting out unneeded characters.
Try this code: Just give $file a relative path to the file and check it.
<?php
// your file to download
$file = path/to/the/file;
header("Expires: 0");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
$ext = pathinfo($file, PATHINFO_EXTENSION);
$basename = pathinfo($file, PATHINFO_BASENAME);
header("Content-type: application/".$ext);
// tell file size
header('Content-length: '.filesize($file));
// set file name
header("Content-Disposition: attachment; filename=\"$basename\"");
readfile($file);
// Exit script. So that no useless data is output-ed.
exit;
?>
Let php calculate the file size and date and other stuff. It is quiet possible that an error can occur while feeding it via an array.
Also in this code I have used pathinfo so as to automatically set the Content-type and file name.
I would like to contribute for someone that was lost with this solution.
What worked for me, has said above by #Kalob Taulien was insert these lines that turn off a compactation algorith builtin that was corrupting my files on download by the script, here is:
#ob_end_clean();
if(ini_get('zlib.output_compression'))
ini_set('zlib.output_compression', 'Off');
After insert that before fpassthru($file) function, the downloads worked well.
I am using soundmanager2 and I have a problem with Safari.
I am successfully streaming protected files (outside the webroot) from PHP to Soundmanager2, using something like this :
//check if user is logged in and has rights on $file
//if yes stream file
if (file_exists($file)) {
$filepath = $file;
$filesize = filesize($filepath);
header("Pragma: no-cache");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header('Content-Disposition: attachment;filename="'.$filename.'"');
header("Content-Transfer-Encoding: binary");
header('Content-Type: audio/mpeg');
header('Content-Length: '.$filesize);
#readfile($filepath);
exit(0);
}
This works fine on Firefox and Chrome, the mp3 files are playing, but in Safari I get :
soundmanager2.js:1190basicMP3Sound0: Using HTML5
soundmanager2.js:1190basicMP3Sound0: play(): Attempting to load
soundmanager2.js:1190basicMP3Sound0: load (/privateaccess/index/1415)
soundmanager2.js:1190basicMP3Sound0: waiting
soundmanager2.js:1190basicMP3Sound0: loadstart
soundmanager2.js:1190basicMP3Sound0: loadedmetadata
soundmanager2.js:1190basicMP3Sound0: HTML5 error, code 3
soundmanager2.js:1188basicMP3Sound0: Failed to load / invalid sound? Zero-length duration reported. (/privateaccess/index/1415)
I only get this error when I stream a file from PHP, it's working with files that are in the webroot (delivered by apache instead of PHP).
If I hit www.myurl.com/privateaccess/index/1415 directly, the file is downloaded, so it really seems like an issue between Safari,Soundmanager2 and PHP file streaming.
Anyone? an idea to fix/workaround this?
You need to support byte range requests. See SoundManager2's Technical Notes on the subject.
Example Request:
GET some.ogg HTTP/1.1
Range: bytes=5210604-5275910
Expected Response:
HTTP/1.1 206 Partial Content
Accept-Ranges: bytes
Content-length: 65307
Content-Range: bytes 5210604-5275910/5275911
Content-Type: audio/ogg
I wrote a download script in PHP as specified below, my script is downloading the files correctly, but I am feeling that the browser(chrome) progress bar is not getting updated properly in regular intervals.
My file is of size 320MB, while downloading that file the progress is getting updated randomly as "11MB, 76MB, 200Mb & 320MB" or "70MB & 320MB" etc.
In most of the sites download progress update is happening in constant chunks like after every MB, so I want to know how we can control the progress update intervals, may be by sending some extra headers or something else.
I want to improve the user experience by updating the progress in constant intervals, so anybody please help me to handle this situation in a proper way.
// HTTP Headers for ZIP File Downloads
// http://perishablepress.com/press/2010/11/17/http-headers-file-downloads/
// file variables
$filename = "Movie Tunes.zip";
$filepath = "files/";
// http headers for zip downloads
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: public,must-revalidate, post-check=0, pre-check=0");
header("Content-Description: File Transfer"); // MIME
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"".$filename."\"");
header("Content-Transfer-Encoding: binary"); // MIME
header("Content-Length: ".filesize($filepath.$filename));
ob_end_flush();
#readfile($filepath.$filename);
Thanks,
Siva
No, you cannot influence when/how the browser updates its download progress bar.
I'm using the following the PHP script to download a 20mb file:
(filepath & filename are set earlier in the script)
$fullPath = $filepath.$filename;
if ($fd = fopen($fullPath, "r")) {
// http headers for zip downloads
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"".$filename."\"");
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($fullPath));
while(!feof($fd)) {
echo(fread($fd, 1024*8));
flush();
if (connection_status()!=0) {
fclose($fd);
die();
}
}
}
fclose($fd);
exit();
It works fine if the download finishes, but if the download is canceled by the user, and they click on the link to re-download, the server is completely unresponsive for several minutes, and then the download will begin. It seems like it is waiting for the script to time out...but isn't the "if (connection_status()!=0)..." part supposed to kil the script?
Any help is much appreciated! Thank you in advance!
I think you're over-engineering your solution somewhat. Try using readfile() instead of your fopen/fread loop.
Better yet, unless there's a compelling reason why you need PHP to mediate the file transfer, don't use a PHP script at all and simply provide a direct link to the file in question.
The best code is always the code you don't have to write.