Working with PHP script files: unreliable downloads?
This post started out as a question from ServerFault ( https://serverfault.com/questions/131156/user-receiving-partial-downloads ), but I figured our PHP script was the culprit. So I am releasing an updated question on what I think is the actual issue.
I am using a php script to check permissions and then serves to upload a file to load my website. Most of the time this works, but recently one user has been having problems with large downloads. It gets ~ 80% downloads for files> 100MB. Also, all downloads from this script do not report file sizes. Also, tests have shown that the same user CAN reliably download each of the failed files if given a direct link (at which point the file size is reported).
Here is the relevant piece of code that we use to serve the file:
header("Content-type:$contenttype");
$len = filesize($filename);
header("Content-Length: $len");
header("Content-Disposition: attachment; filename=".$title.".".$ext);
readfile($filename);
Please note that $ contenttype, $ filename, $ title and $ ext are configured correctly before we move on here. They have been tested three times. None of these are a problem. Additionally, $ len provides the correct file size.
While researching this issue, I came across this post: Content-Length header is always null
It seems that I am facing the same problem. When I use the script, I get the encoded encoding in the file and the content size is not set. I am guessing something is wrong with large downloads that caused it to get a zero length chunk to the end of the file.
Here the headers look like a direct request:
http://www.grinderschool.com/videos/zfff5061b65ae00e8b21/KillsAids021.wmv
GET /videos/zfff5061b65ae00e8b21/KillsAids021.wmv HTTP/1.1
Host: www.grinderschool.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Referer: http://www.grinderschool.com/phpBB3/viewtopic.php?f=14&p=29468
Cookie: style_cookie=printonly; phpbb3_7c544_u=2; phpbb3_7c544_k=44b832912e5f887d; phpbb3_7c544_sid=e8852df42e08cc1b2250300c2897f78f; __utma=174624884.2719561324781918700.1251850714.1270986325.1270989003.575; __utmz=174624884.1264524375.411.12.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=low%20stakes%20poker%20videos; phpbb3_cmviy_k=; phpbb3_cmviy_u=2; phpbb3_cmviy_sid=d8df5c0943863004ca40ef9c392d371d; __utmb=174624884.4.10.1270989003; __utmc=174624884
Pragma: no-cache
Cache-Control: no-cache
HTTP/1.1 200 OK
Date: Sun, 11 Apr 2010 12:57:41 GMT
Server: Apache/2.2.14 (Unix) mod_ssl/2.2.14 OpenSSL/0.9.8l DAV/2 mod_auth_passthrough/2.1 FrontPage/5.0.2.2635
Last-Modified: Sun, 04 Apr 2010 12:51:06 GMT
Etag: "eb42d6-7d9b843-48368aa6dc280"
Accept-Ranges: bytes
Content-Length: 131708995
Keep-Alive: timeout=10, max=30
Connection: Keep-Alive
Content-Type: video/x-ms-wmv
And this is how they look for the request my script answered:
http://www.grinderschool.com/download_video_test.php?t=KillsAids021&format=wmv
GET /download_video_test.php?t=KillsAids021&format=wmv HTTP/1.1
Host: www.grinderschool.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Cookie: style_cookie=printonly; phpbb3_7c544_u=2; phpbb3_7c544_k=44b832912e5f887d; phpbb3_7c544_sid=e8852df42e08cc1b2250300c2897f78f; __utma=174624884.2719561324781918700.1251850714.1270986325.1270989003.575; __utmz=174624884.1264524375.411.12.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=low%20stakes%20poker%20videos; phpbb3_cmviy_k=; phpbb3_cmviy_u=2; phpbb3_cmviy_sid=d8df5c0943863004ca40ef9c392d371d; __utmb=174624884.4.10.1270989003; __utmc=174624884
HTTP/1.1 200 OK
Date: Sun, 11 Apr 2010 12:58:02 GMT
Server: Apache/2.2.14 (Unix) mod_ssl/2.2.14 OpenSSL/0.9.8l DAV/2 mod_auth_passthrough/2.1 FrontPage/5.0.2.2635
X-Powered-By: PHP/5.2.11
Content-Disposition: attachment; filename=KillsAids021.wmv
Vary: Accept-Encoding
Content-Encoding: gzip
Keep-Alive: timeout=10, max=30
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: video/x-ms-wmv
So the question is ... what can I do to get it loaded correctly from the script? Again, for 99% of users, it works as it is (although I now find it annoying that no file sizes are reported and therefore no estimate of the time to download).
source to share
This is your GZIP compression. When you specify the length of the content, but include compression, it all whimpers. This happened to me several times: try turning it off in your script.
Typically, you include it:
ob_start("ob_gzhandler");
... so just comment this line out. If it's not in your code, it might be in your php.ini or apache.conf / conf.d file somewhere.
Hope this helps!
source to share
Content-Encoding: gzip
Hmm. Presumably PHP zlib.output_compression does this. (Not like Apache mod_deflate.)
Try disabling it and see if that forces you to force encoded encoding. You don't want to compress the download of a WMV type file that is already heavily compressed.
However, the coded coding only explains the lack of a size report. The download should still work. Is it possible that you have been hit by a timeout (like PHP set_time_limit or Apache Timeout )?
source to share
If it's a script, have you tried using a substitute function for the readfile () function that reads and outputs a bit at a time? The reason for this might be that the memory limit has been reached somewhere and it is failing.
From http://php.net/manual/en/function.readfile.php :
function readfile_chunked ($filename) {
$chunksize = 1*(1024*1024); // how many bytes per chunk
$buffer = '';
$handle = fopen($filename, 'rb');
if ($handle === false) {
return false;
}
while (!feof($handle)) {
$buffer = fread($handle, $chunksize);
print $buffer;
}
return fclose($handle);
}
Also, try flushing the output as often as you can.
source to share