I've the next script to allow a customer download personal files:

header( 'Content-Type: application/octet-stream' );
header( 'Content-Transfer-Encoding: binary' );
header( 'Content-Disposition: attachment; filename=' . $fileName );
header( 'Content-Length: ' . filesize( $filePath ) );
header( 'Content-Description: Download' );
header( 'Cache-Control: private' );
header( 'Pragma: no-cache' );
header( 'Expires: 0' );

readfile( $filePath );

It does not work perfectly. (I have also place the filename in quotes, same result).

It reacts very slow, and often the downloads even halt. In Opera especially, it halts on 99% from the download. It sometimes even immediately shows 99% completed, it begins installing and halts around 34%.

The server is really a shared host, Mac OS X server.

With using Firefox's Live HTTP headers add-on, I have observed the server adds aditional headers towards the response:

HTTP/1.1 200 OK
Date: Thu, 18 Feb 2010 09:27:25 GMT
Server: Apache
X-Powered-By: PHP/5.2.12
Content-Transfer-Encoding: binary
Content-Disposition: attachment; filename=test.psd
Content-Length: 398635
Content-Description: Download
Cache-Control: private
Pragma: no-cache
Expires: 0
Content-Encoding: gzip // <-- expecially this one,
Vary: Accept-Encoding // <-- this one,
MS-Author-Via: DAV // <-- and this one
Keep-Alive: timeout=10, max=100
Connection: Keep-Alive
Content-Type: application/octet-stream

Could these be the reason for the issue?

After I run the script on my small localhost everything works fine. Also, after i directly download files out of this host, the rate can also be fine and fluent.

I am really rather unaware on that one. Your assistance is appeciated. Thanks ahead of time.


I believe I've simplified the issue lower towards the bottleneck. The webserver instantly gzip compresses the output. After I removed the Content-Length header from the PHP script everything began installing smooth. This will make sense: The need for the Content-Length does not match the particular gzipped output any longer. In PHP I just read the uncompressed filesize to create the Content-Length header, but later on, Apache compresses it, which is most likely in which the browsers clogged.

I'll follow this track of an issue concerning how to set the right Content-Length header size once the webserver instantly gzip compresses output.

Try unsetting the gzip-Content-Encoding.

Use ob_start() in the beginning of the script before setting your headers, use @ob_end_clean(); and immediately after it, explicitely set header("Content-Encoding:"); to try and unset any gzip-Encoding that may are available in. In the finish of the file place @ob_end_flush();.

The output loading functions are handy to create the header-setting more failsafe, but most likely not associated with your condition. I just be sure you went into problems inside a setup in which the attaching PHP code used doctor_gzhandler and that i required to unset it.

I personally use the code below and delay pills work. Suggesting the reality I didn't undersyand yet correctly all of the header which i send, I still was without time to research, I discovered explanations in:


http://www.opendesigns.org/forum/discussion/1437/php-download-counter/#pgbottom http://www.webdeveloper.com/forum/showthread.php?t=115815&highlight=PHP+download+counter http://php.net/manual/en/function.header.php#83384

anyway it really works:

   TODO: still to be read and better understood.

   //no caching (I don't uderstand what is this part useful for)
   header("Pragma: public"); //?
   header("Expires: 0"); //?
   header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); //?
   header("Cache-Control: private", false); //?

   //sending download file
   header("Content-Type: application/octet-stream"); //application/zip can use application/octet-stream that is more generic it works because in now days browsers are able to detect file anyway
   header("Content-Disposition: attachment; filename=\"" . basename($file_serverfullpath) . "\""); //ok
   header("Content-Transfer-Encoding: binary"); //?
   header("Content-Length: " . filesize($file_serverfullpath)); //ok