使用PHP发送文件时可以断点续传吗?
我们使用 PHP 脚本进行隧道文件下载,因为我们不想公开可下载文件的绝对路径:
header("Content-Type: $ctype");
header("Content-Length: " . filesize($file));
header("Content-Disposition: attachment; filename=\"$fileName\"");
readfile($file);
不幸的是,我们注意到最终用户无法恢复通过此脚本传递的下载。
有什么办法可以使用这种基于 PHP 的解决方案来支持断点续传下载吗?
We are using a PHP scripting for tunnelling file downloads, since we don't want to expose the absolute path of downloadable file:
header("Content-Type: $ctype");
header("Content-Length: " . filesize($file));
header("Content-Disposition: attachment; filename=\"$fileName\"");
readfile($file);
Unfortunately we noticed that downloads passed through this script can't be resumed by the end user.
Is there any way to support resumable downloads with such a PHP-based solution?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(14)
您需要做的第一件事是在所有响应中发送 Accept-Ranges: bytes 标头,以告诉客户端您支持部分内容。然后,如果收到带有
Range: bytes=xy
标头的请求(x
和y
是数字),您将解析范围客户端发出请求,照常打开文件,向前查找x
字节并发送接下来的y
-x
字节。还将响应设置为HTTP/1.0 206 Partial Content
。在没有测试过任何东西的情况下,这或多或少是可行的:
我可能错过了一些明显的东西,而且我肯定忽略了一些潜在的错误来源,但这应该是一个开始。
这里有一个部分内容的描述,我在 fread 的文档页面上找到了一些有关部分内容的信息。
The first thing you need to do is to send the
Accept-Ranges: bytes
header in all responses, to tell the client that you support partial content. Then, if request with aRange: bytes=x-y
header is received (withx
andy
being numbers) you parse the range the client is requesting, open the file as usual, seekx
bytes ahead and send the nexty
-x
bytes. Also set the response toHTTP/1.0 206 Partial Content
.Without having tested anything, this could work, more or less:
I may have missed something obvious, and I have most definitely ignored some potential sources of errors, but it should be a start.
There's a description of partial content here and I found some info on partial content on the documentation page for fread.
编辑 2017/01 - 我用 PHP >=7.0 编写了一个库来执行此操作 https: //github.com/DaveRandom/Resume
编辑 2016/02 - 代码完全重写为一组模块化工具和示例用法,而不是单一的 功能。下面评论中提到的更正已被纳入。
一个经过测试的、有效的解决方案(很大程度上基于上面 Theo 的答案),它在一组几个独立工具中处理可恢复下载。此代码需要 PHP 5.4 或更高版本。
该解决方案仍然只能处理每个请求的一个范围,但在我能想到的标准浏览器的任何情况下,这都不会引起问题。
用法示例:
EDIT 2017/01 - I wrote a library to do this in PHP >=7.0 https://github.com/DaveRandom/Resume
EDIT 2016/02 - Code completely rewritten to a set of modular tools an an example usage, rather than a monolithic function. Corrections mentioned in comments below have been incorporated.
A tested, working solution (based heavily on Theo's answer above) which deals with resumable downloads, in a set of a few standalone tools. This code requires PHP 5.4 or later.
This solution can still only cope with one range per request, but under any circumstance with a standard browser that I can think of, this should not cause a problem.
Example usage:
是的。支持字节范围。请参阅 RFC 2616 第 14.35 节。
它基本上意味着您应该读取
Range
标头,并从指定的偏移量开始提供文件。这意味着您不能使用 readfile(),因为它服务于整个文件。相反,首先使用 fopen(),然后使用 fseek() 到正确的位置,然后使用 fpassthru() 来提供文件。
Yes. Support byteranges. See RFC 2616 section 14.35 .
It basically means that you should read the
Range
header, and start serving the file from the specified offset.This means that you can't use readfile(), since that serves the whole file. Instead, use fopen() first, then fseek() to the correct position, and then use fpassthru() to serve the file.
这100%有效,超级检查一下
我正在使用它,没有任何问题了。
This works 100% super check it
I am using it and no problems any more.
解决此问题的一个非常好的方法是使用 mod_xsendfile Apache 模块,而无需“编写自己的”PHP 代码。然后在 PHP 中,您只需设置适当的标头即可。 Apache 开始做它的事情了。
A really nice way to solve this without having to "roll your own" PHP code is to use the mod_xsendfile Apache module. Then in PHP, you just set the appropriate headers. Apache gets to do its thing.
如果您愿意安装新的 PECL 模块,使用 PHP 支持可恢复下载的最简单方法是通过
http_send_file()
,如以下来源:http://www.php.net/manual/en/function.http-send-file.php
我们用它来提供数据库存储的内容,它的作用就像一个魅力!
If you're willing to install a new PECL module, the easiest way to support resumeable downloads with PHP is through
http_send_file()
, like thissource : http://www.php.net/manual/en/function.http-send-file.php
We use it to serve database-stored content and it works like a charm !
是的,您可以使用 Range 标头。您需要向客户端提供 3 个以上的标头才能进行完整下载:
与中断下载相比,您需要通过以下方式检查范围请求标头:
在这种情况下,不要忘记使用 206 状态代码提供内容:
您将从请求标头获取 $start 和 $to 变量,并使用 fseek() 查找文件中的正确位置。
Yes, you can use the Range header for that. You need to give 3 more headers to the client for a full download:
Than for an interrupted download you need to check the Range request header by:
And in this case don't forget to serve the content with 206 status code:
You'll get the $start and $to variables from the request header, and use fseek() to seek to the correct position in the file.
最上面的答案有各种错误。
bytes ab
应该表示[a, b]
而不是[a, b)
,并且bytes a-
是没有处理。这是我修改后的代码:
The top answer has various bugs.
bytes a-b
should mean[a, b]
instead of[a, b)
, andbytes a-
is not handled.Here's my modified code:
这对我来说非常有效: https://github.com/pomle/php-serveFilePartial
This worked very well for me: https://github.com/pomle/php-serveFilePartial
启用小型 Composer 的类,其工作方式与 pecl http_send_file 相同。这意味着支持可恢复下载和限制。 https://github.com/diversen/http-send-file
Small composer enabled class which works the same way as pecl http_send_file. This means support for resumable downloads and throttle. https://github.com/diversen/http-send-file
您可以使用以下代码在任何浏览器上支持字节范围请求
You could use the below code for byte range request support across any browser
HTTP 中的恢复下载是通过
Range
标头完成的。如果请求包含Range
标头,并且其他指示符(例如If-Match
、If-Unmodified-Since
)表明内容尚未自下载开始以来没有改变,您提供 206 响应代码(而不是 200),指示您在Content-Range
标头中返回的字节范围,然后在响应体。但我不知道如何在 PHP 中做到这一点。
Resuming downloads in HTTP is done through the
Range
header. If the request contains aRange
header, and if other indicators (e.g.If-Match
,If-Unmodified-Since
) indicate that the content hasn't changed since the download was started, you give a 206 response code (rather than 200), indicate the range of bytes you're returning in theContent-Range
header, then provide that range in the response body.I don't know how to do that in PHP, though.
谢谢西奥!你的方法不能直接用于流式传输 divx,因为我发现 divx 播放器正在发送类似 bytes=9932800 的范围 -
但它向我展示了如何做到这一点,所以谢谢:D
Thanks Theo! your method did not directly work for streaming divx because i found the divx player was sending ranges like bytes=9932800-
but it showed me how to do it so thanks :D
我创建了一个用于提供文件的库,支持条件(不要再次下载文件,除非文件已更改)和范围(暂停和恢复下载)请求。它甚至可以与虚拟文件系统配合使用,例如 Flysystem。
在这里查看:FileWaiter
示例用法:
I've created a library for serving files with support for conditional (don't download file again unless it has changed) and ranged (pause and resume download) requests. It even works with virtual file systems, such as Flysystem.
Check it out here: FileWaiter
Example usage: