我们正在尝试将mod_xsendfile与Apache一起使用,以有效地处理大文件下载(> 1 GB)。安装后,配置如下:
<IfModule mod_xsendfile.c>
<Directory "/abs_path/to/dl">
XSendFile on
XSendFilePath /abs_path/to/files_dir
</Directory>
</IfModule>
下载脚本没什么特别的,只是检查是否存在要下载的文件,并根据文档设置标题,如下所示:
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . basename($file) . '"');
header("X-Sendfile: " . $file);
不间断的下载可以与我们测试过的任何用户代理一起使用,并且HTTP-Range可以很好地处理除Firefox以外的所有用户(测试版本27和28)。 Firefox可以暂停下载,但每次都恢复失败。
这些是使用Live HTTP标头扩展程序捕获的http标头:
初次下载:
http://www.oursite.com/dl/test-xs.php?ID=TestFileID
GET /dl/test-xs.php?ID=TestFileID HTTP/1.1
Host: www.oursite.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-gb,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: some cookie string...
Connection: keep-alive
HTTP/1.1 200 OK
Date: Tue, 25 Mar 2014 10:22:46 GMT
Server: Apache
X-Powered-By: PHP/5.3.28
Content-Disposition: attachment; filename="TestFile.ext"
Last-Modified: Sun, 02 Mar 2014 18:20:36 GMT
Content-Length: 84406272
Connection: close
Content-Type: application/octet-stream
...当Firefox尝试恢复时:
http://www.oursite.com/dl/test-xs.php?ID=TestFileID
GET /dl/test-xs.php?ID=TestFileID HTTP/1.1
Host: www.oursite.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-gb,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: same cookie string...
Connection: keep-alive
Range: bytes=11238434-
If-Unmodified-Since: Sun, 02 Mar 2014 18:20:36 GMT
...服务器返回404
HTTP/1.1 404 Not Found
Date: Tue, 25 Mar 2014 10:23:03 GMT
Server: Apache
X-Powered-By: PHP/5.3.28, PHP/5.3.28
Content-Disposition: attachment; filename="TestFile.ext"
X-Sendfile: /abs_path/to/files_dir/TestFile.ext
X-Pingback: http://www.oursite.com/xmlrpc.php
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Cache-Control: no-cache, must-revalidate, max-age=0
Pragma: no-cache
Connection: close
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8
...这显然导致Firefox无法恢复下载(现在只能取消并从头开始重新启动)。
考虑到其他浏览器和下载管理器中的所有内容都符合预期,我们尝试过:
修改:
经过一些更多的测试后发现问题归结为Firefox在恢复下载时发送的If-Unmodified-Since
标题。尽管此标头已正确设置为从Apache收到的Last-Modified
响应标头的值,但服务器由于某种原因不喜欢它,并以404
响应。通过更改.htaccess:
If-Unmodified-Since
标头
<Files test-xs.php>
RequestHeader unset If-Unmodified-Since
</Files>
...简历在包括Firefox在内的所有地方都能正常运行。
如果要同时修改要下载的文件,这种方法当然是不正确的,但是它为我们完成了工作,因为我们使用不同的约定来提供同一文件的较新版本。
这显然更像是一个黑客而不是正确的实现,所以不确定这是否应该被标记为答案,我将其留作原始问题的补充。
显然出现了新问题:
答案 0 :(得分:1)
@alternize's solution大部分都是正确的,但提供的代码段为:
SetEnvIf Range .+ HAS_RANGE_HEADER
RequestHeader unset If-Range env=!HAS_RANGE_HEADER
RequestHeader unset If-Unmodified-Since env=!HAS_RANGE_HEADER
实际上不会取消包含Range
标头的请求的指定标头。
“!”否定匹配,因此上述代码段实际上会为所有请求取消设置这些标题,但带有Range
标题的请求。
要从具有If-Range
标头的请求中正确删除If-Unmodified-Since
和Range
标头,您可以使用相同的指令,但删除这样的否定:
SetEnvIf Range .+ HAS_RANGE_HEADER
RequestHeader unset If-Range env=HAS_RANGE_HEADER
RequestHeader unset If-Unmodified-Since env=HAS_RANGE_HEADER
这已在apache 2.2.15上得到确认。
答案 1 :(得分:0)
mod_xsendfile似乎不适用于浏览器发送的一些缓存标头。例如,当使用“Range”标题恢复下载时,chrome会发送“If-Range”标题:
第一个请求标题:
GET /foo.webm
Range: bytes=0-
第一个回复标题:
206 Partial Content
Content-Length: 54376097
Content-Range: bytes 0-54376096/54376097
Content-Type: video/webm
ETag: "78976c9d1a595cba56e24bec6f2f1178"
=&GT;视频开始播放
现在用户将擦洗到文件中的稍后位置
第二个请求标题:
GET /foo.webm
If-Range: "78976c9d1a595cba56e24bec6f2f1178"
Range: bytes=54373845-54376096
第二个回复标题:
200 OK
Content-Length: 54376097
ETag: "78976c9d1a595cba56e24bec6f2f1178"
=&GT;发送整个文件而不是请求的字节范围
解决方法强>
如果存在“Range”标题,取消设置有问题的请求标头:
SetEnvIf Range .+ HAS_RANGE_HEADER
RequestHeader unset If-Range env=!HAS_RANGE_HEADER
RequestHeader unset If-Unmodified-Since env=!HAS_RANGE_HEADER