2

这篇文章最初是关于 ServerFault ( https://serverfault.com/questions/131156/user-receiving-partial-downloads ) 的一个问题,但我确定我们的 php 脚本是罪魁祸首。所以我在这里发布一个关于我认为是实际问题的更新问题。

我正在使用 php 脚本来验证权限,然后提供一个文件供我网站的用户下载。大多数情况下,这是可行的,但最近一位用户遇到了较大下载量的问题。对于大于 100MB 的文件,他只获得了约 80% 的下载量。此外,此脚本的所有下载都无法报告文件大小。此外,测试表明,如果给定直接链接(此时报告文件大小),同一用户可以可靠地下载每个失败的文件。

这是我们用来提供文件的相关代码片段:

header("Content-type:$contenttype");
$len = filesize($filename);
header("Content-Length: $len");
header("Content-Disposition: attachment; filename=".$title.".".$ext);
readfile($filename);

请注意,在我们到达这里之前,$contenttype、$filename、$title 和 $ext 都已正确设置。这些都经过了三重检查。他们都不是问题。此外, $len 确实提供了正确的文件大小。

在研究这个问题时,我遇到了这篇文章:Content-Length header always zero

看来我遇到了同样的问题。当我使用脚本时,我对文件进行了分块编码,并且没有为内容长度设置大小。我假设大型下载出现问题,导致他在文件结束之前获得零长度块。

以下是直接请求的标头:

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

这是我的脚本回答的请求的样子:

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

所以问题是......我该怎么做才能使脚本的下载正常工作?同样,对于 99% 的用户来说,它按原样工作(尽管我现在觉得很烦人,因为没有报告文件大小,因此无法计算关于下载的时间估计)。

4

3 回答 3

2
Content-Encoding: gzip

唔。大概 PHP 的zlib.output_compression正在这样做。(看起来不像 Apache 的 mod_deflate。)

尝试将其关闭并查看是否是强制分块编码的原因。您不想压缩已经高度压缩的 WMV 等文件类型的下载。

但是,分块编码只能解释缺少大小报告。下载应该仍然有效。您是否有可能被超时(例如 PHP 的set_time_limit或 Apache Timeout)击中?

于 2010-04-11T14:03:17.133 回答
2

这是您的 GZIP 压缩。当您指定内容长度但打开压缩时,它会将所有内容都搞砸。这发生在我身上几次:尝试在你的脚本中关闭它。

通常你会打开它:

ob_start("ob_gzhandler");

...所以只需注释掉该行。如果这不在您的代码中,则很可能在您的 php.ini 文件或 apache.conf/conf.d 中存在设置。

希望这可以帮助!

于 2010-04-11T14:05:50.717 回答
0

如果是脚本,那么您是否尝试过使用 readfile() 函数的替代函数来一次读取和输出一点?这背后的原因可能是某个地方达到了内存限制并且它失败了。

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);
} 

此外,请尝试尽可能频繁地刷新输出。

于 2010-04-11T14:08:05.800 回答