PHP实现文件下载断点续传详解


Posted in PHP onOctober 15, 2014

如果我们的网站提供文件下载的服务,那么通常我们都希望下载可以断点续传(Resumable Download),也就是说用户可以暂停下载,并在未来的某个时间从暂停处继续下载,而不必重新下载整个文件。

通常情况下,Web服务器(如Apache)会默认开启对断点续传的支持。因此,如果直接通过Web服务器来提供文件的下载,可以不必做特别的配置,即可享受到断点续传的好处。由于这些文件直接通过Web服务器来提供下载,后端脚本无法对这个下载过程进行控制。这对于仅提供公开、静态文件的网站来说不是问题,但对于需要提供私有、动态文件的网站来说,直接通过Web服务器来提供下载就无法满足需求了。这时,就需要在编写后台脚本程序时,加入对断点续传的支持。

本文将以PHP为例,简要介绍实现文件下载断点续传的方法。

原理

断点续传的原理还是比较直观的。

HTTP协议规定了如何传输某个资源的一部分,而不是全部。比如,有一个文件的大小是1000字节,浏览器可以只请求该文件的前300个字节,或者只请求第500到第1000个字节。通过这种方式,就可以不必在一次请求中传输某个资源的全部内容,而是发起多次请求,每次仅请求其中的一部分内容。等所有这些请求都返回之后,再把得到的内容一块一块的拼接起来得到完整的资源。

实现断点续传就是要利用HTTP协议的上述特性。当用户暂停下载的时候,浏览器会记录已经下载到什么位置,当用户在未来某一时间恢复下载时,就可以从上次暂停的位置继续下载,而不必从头开始。

实现

由于部分传输不是强制的,服务器可以支持也可以不支持,所以,我们需要在程序中告诉浏览器,它请求的资源是否支持部分传输。这可以通过设置HTTP的 Accept-Ranges 响应头信息来实现。PHP代码如下:

header('Accept-Ranges: bytes');

Accept-Ranges: bytes 告诉浏览器,该资源支持以字节为单位的部分传输。这个响应头需要附加在支持部分传输的所有资源上。

当接受到一个请求时,我们需要从浏览器的请求中提取浏览器具体是在请求资源的哪一个部分。这个信息是通过 Range 请求头来传递的。在PHP中,它被存储在$_SERVER['HTTP_RANGE']中。我们需要检查这个变量是否定义了,如果定义了,则使用该值,否则,就将range设为整个资源。

$range = "0-". ($content_length-1);

if(isset($_SERVER['HTTP_RANGE'])){

    $range = $_SERVER['HTTP_RANGE'];

}

接下来,就需要分析 $range 的值,来决定返回资源的哪一部分内容。可能的取值示例:
100-200 // 第100到第200字节

500-    // 第500字节到文件末尾

-1000   // 最后的1000个字节

这里需要注意,得到一个Range之后,你需要对它的取值进行检验,包括:

1.开始位置非负
2.结束位置需要大于开始位置
3.开始位置需要小于文件长度减一 (因为这里的位置索引是从0开始的)
4.若结束位置大于文件长度减一,则需要把它的值设置为文件长度减一

如果Range的取值不合法,则需要终止程序并告知浏览器:

header('HTTP/1.1 416 Requested Range Not Satisfiable');

为了保持文章简洁,具体的校验代码这里就不提供了。下面假定你已经校验了Range的取值,并得到了 $start 和 $end 两个变量,分别表示开始位置和结束位置。

接下来要做的就是把文件的对应部分的内容发送给浏览器。不过要注意的是,这里涉及到需要发送多个HTTP响应头信息,具体如下:

header('HTTP/1.1 206 Partial Content');

header('Accept-Ranges: bytes');

header("Content-Range: bytes $start-$end/$filesize");

$length = $end - $start + 1;

header("Content-Length: $length");
/* 输出文件的指定部分 */

这里的$length需要注意一下,它的取值是本次传输的内容的长度,而不是整个文件的长度。另外需要注意的一点是,这里的HTTP状态码是206,不是200。

总结

文件下载的断点续传实际上是利用了HTTP协议中对传输部分文件的支持。而HTTP协议的这一特性不仅可以用于实现断点续传,客户端程序也可以利用它来实现多线程下载。

在实现断点续传的过程中,需要注意正确设置各种HTTP头信息。错误的头信息将导致用户下载到的文件损坏,无法使用。

PHP 相关文章推荐
PHP简单系统查询模块代码打包下载
Jun 07 PHP
php获取网页内容方法总结
Dec 04 PHP
PHPMYADMIN 简明安装教程 推荐
Mar 07 PHP
php中突破基于HTTP_REFERER的防盗链措施(stream_context_create)
Mar 29 PHP
PHP下打开phpMyAdmin出现403错误的问题解决方法
May 23 PHP
关于初学PHP时的知识积累总结
Jun 07 PHP
PHP获取时间排除周六、周日的两个方法
Jun 30 PHP
php简单实现发送带附件的邮件
Jun 10 PHP
详解PHP处理密码的几种方式
Nov 30 PHP
浅谈关于PHP解决图片无损压缩的问题
Sep 01 PHP
yii2.0整合阿里云oss的示例代码
Sep 19 PHP
laravel框架数据库操作、查询构建器、Eloquent ORM操作实例分析
Dec 20 PHP
PHP多进程编程实例
Oct 15 #PHP
PHP实现采集中国天气网未来7天天气
Oct 15 #PHP
跟我学Laravel之视图 & Response
Oct 15 #PHP
跟我学Laravel之请求与输入
Oct 15 #PHP
跟我学Laravel之路由
Oct 15 #PHP
跟我学Laravel之请求(Request)的生命周期
Oct 15 #PHP
跟我学Laravel之配置Laravel
Oct 15 #PHP
You might like
PHP实现动态压缩js与css文件的方法
2018/05/02 PHP
PHP中PDO事务处理操作示例
2018/05/02 PHP
初学js插入节点appendChild insertBefore使用方法
2011/07/04 Javascript
createElement与createDocumentFragment的点点区别小结
2011/12/19 Javascript
Jquery颜色选择器ColorPicker实现代码
2012/11/14 Javascript
通过jQuery源码学习javascript(二)
2012/12/27 Javascript
javascript禁用Tab键脚本实例
2013/11/22 Javascript
JavaScript中的函数的两种定义方式和函数变量赋值
2014/05/12 Javascript
最流行的Node.js精简型和全栈型开发框架介绍
2015/02/26 Javascript
jQuery实现下滑菜单导航效果代码
2015/08/25 Javascript
Javascript基础回顾之(二) js作用域
2017/01/31 Javascript
jQuery排序插件tableSorter使用方法
2017/02/10 Javascript
使用jquery datatable和bootsrap创建表格实例代码
2017/03/17 Javascript
如何选择jQuery版本 1.x? 2.x? 3.x?
2017/04/01 jQuery
zTree树形菜单交互选项卡效果的实现方法
2017/12/25 Javascript
vue实现在表格里,取每行的id的方法
2018/03/09 Javascript
JavaScript继承与多继承实例分析
2018/05/26 Javascript
vue中v-for循环给标签属性赋值的方法
2018/10/18 Javascript
如何能分清npm cnpm npx nvm
2019/01/17 Javascript
js设置默认时间跨度过程详解
2019/07/17 Javascript
BootStrap前端框架使用方法详解
2020/02/26 Javascript
jquery实现简单拖拽效果
2020/07/20 jQuery
python中计算一个列表中连续相同的元素个数方法
2018/06/29 Python
Python GUI编程完整示例
2019/04/04 Python
python交易记录链的实现过程详解
2019/07/03 Python
利用python实现汉字转拼音的2种方法
2019/08/12 Python
将python文件打包exe独立运行程序方法详解
2020/02/12 Python
全球度假村:Club Med
2017/11/27 全球购物
毕业自我鉴定范文
2013/11/06 职场文书
业务主管岗位职责
2013/11/20 职场文书
公司成立感言
2014/01/11 职场文书
医科大学毕业生自荐信
2014/02/03 职场文书
入党团支部推荐意见
2015/06/02 职场文书
学校运动会简讯
2015/07/20 职场文书
爱国主题班会教案
2015/08/14 职场文书
Mysql 如何实现多张无关联表查询数据并分页
2021/06/05 MySQL