PHP 文件服务脚本:下载不可靠?

发布于 2024-08-28 09:56:46 字数 4055 浏览 3 评论 0原文

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

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

以下是我们用于提供文件服务的相关代码片段:

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 始终为零

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

这是直接请求的标头的样子:

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% 的用户来说,它按原样工作(尽管我觉得现在很烦人,因为没有报告文件大小,因此无法计算下载的时间估计)。

This post started as a question on ServerFault ( https://serverfault.com/questions/131156/user-receiving-partial-downloads ) but I determined that our php script was the culprit. So I'm issuing an updated question here about what I believe is the actual issue.

I am using a php script to verify permissions and then serve up a file for users of my website to download. Most of the time, this works, but recently one user has been seeing problems with larger downloads. He is only getting ~80% of downloads for files that are > 100MB in size. Also, all downloads from this script fail to report a filesize. Further, tests revealed that the same user COULD reliably download each of the failed files if given a direct link (at which point the filesize is reported).

Here's the relevant snippet of code that we are using to serve the file:

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

Note that $contenttype, $filename, $title, and $ext are all set correctly before we get here. These have been triple-checked. None of them are the problem. Also, $len does provide the correct filesize.

While researching this issue, I came across this post: Content-Length header always zero

It seems that I am encountering the same issue. When I use the script, I get chunked encoding on the file and no size is set for content-length. I'm hypothesizing that something is going wrong on the large downloads, leading him to get a zero-length chunk before the end of the file.

Here's what the headers look like for 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 here's what they look like for the request answered by my script:

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 make downloads from the script work properly? Again, for 99% of users, it works as is (though I find it annoying now that no filesize is reported and thus that no time estimate can be computed about the download).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

彼岸花似海 2024-09-04 09:56:46

这是您的 GZIP 压缩。当您指定内容长度但打开压缩时,一切都会变得混乱。这种情况在我身上发生过几次:尝试在脚本中将其关闭。

一般来说,您可以使用以下命令打开它:

ob_start("ob_gzhandler");

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

希望这有帮助!

It's your GZIP compression. When you specify a content length but turn compression on, it gums everything up. It's happened to me a few times: try turning it off in your script.

Generally you'd turn it on with:

ob_start("ob_gzhandler");

...so just comment that line out. If that's not in your code, chances are there's a setting in either your php.ini file somewhere or your apache.conf/conf.d.

Hope this helps!

邮友 2024-09-04 09:56:46
Content-Encoding: gzip

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

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

然而,分块编码只能解释大小报告的缺失。下载应该仍然有效。您是否可能遇到超时(例如 PHP 的 set_time_limit,或 Apache 超时)?

Content-Encoding: gzip

Hmm. Presumably PHP's zlib.output_compression is doing that. (Doesn't look like Apache's mod_deflate.)

Try turning it off and seeing if it's that that's forcing chunked encoding. You don't want to compress the download of a filetype like WMV which is already highly compressed.

However, chunked encoding would only explain the lack of size report. The download should still work. Is it possible you're being hit by a timeout (eg. PHP's set_time_limit, or Apache Timeout)?

花想c 2024-09-04 09:56:46

如果是脚本,那么您是否尝试使用一次读取和输出一点的 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);
} 

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

If it is the script then have you tried using a substitute function for the readfile() function that reads and outputs a bit at a time? The reasoning behind this could be that a memory limit is reached somewhere and it fails.

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 to flush the output as often as you can.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文