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 相关文章推荐
JAVA/JSP学习系列之四
Oct 09 PHP
php合并数组array_merge函数运算符加号与的区别
Oct 31 PHP
PHP 数据结构 算法描述 冒泡排序 bubble sort
Jul 10 PHP
php+ajax实现图片文件上传功能实例
Jun 17 PHP
ThinkPHP实例化模型的四种方法概述
Aug 22 PHP
php使用ereg验证文件上传的方法
Dec 16 PHP
php中get_cfg_var()和ini_get()的用法及区别
Mar 04 PHP
php版微信数据统计接口用法示例
Oct 12 PHP
PHP用户验证和标签推荐的简单使用
Oct 31 PHP
PHP函数rtrim()使用中的怪异现象分析
Feb 24 PHP
php使用crypt()函数进行加密
Jun 08 PHP
yii2.0整合阿里云oss的示例代码
Sep 19 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
ThinkPHP的模版中调用session数据的方法
2014/07/01 PHP
Nigma vs Alliance BO5 第一场2.14
2021/03/10 DOTA
学习YUI.Ext 第四天--对话框Dialog的使用
2007/03/10 Javascript
33种Javascript 表格排序控件收集
2009/12/03 Javascript
使用js操作css实现js改变背景图片示例
2014/03/10 Javascript
一个奇葩的最短的 IE 版本判断JS脚本
2014/05/28 Javascript
JS获取子窗口中返回的数据实现方法
2016/05/28 Javascript
浅谈JavaScript for循环 闭包
2016/06/22 Javascript
在localStorage中存储对象数组并读取的方法
2016/09/24 Javascript
JS实现拖动滚动条评分的效果代码分享
2016/09/29 Javascript
jquery html5 视频播放控制代码
2016/11/06 Javascript
Vue.JS项目中5个经典Vuex插件
2017/11/28 Javascript
JS生成随机打乱数组的方法示例
2017/12/23 Javascript
Vue.js中的组件系统
2019/05/30 Javascript
webpack3升级到webpack4遇到问题总结
2019/09/30 Javascript
微信小程序新闻网站详情页实例代码
2020/01/10 Javascript
[01:02:48]2018DOTA2亚洲邀请赛 4.1 小组赛 A组 LGD vs OG
2018/04/02 DOTA
python基于mysql实现的简单队列以及跨进程锁实例详解
2014/07/07 Python
初步探究Python程序的执行原理
2015/04/11 Python
使用Python脚本实现批量网站存活检测遇到问题及解决方法
2016/10/11 Python
Python 中的range(),以及列表切片方法
2018/07/02 Python
Python之指数与E记法的区别详解
2019/11/21 Python
Python实现企业微信机器人每天定时发消息实例
2020/02/25 Python
python的reverse函数翻转结果为None的问题
2020/05/11 Python
Django中Q查询及Q()对象 F查询及F()对象用法
2020/07/09 Python
Python系统公网私网流量监控实现流程
2020/11/23 Python
CSS3 media queries + jQuery实现响应式导航
2016/09/30 HTML / CSS
JD Sports法国:英国篮球和运动时尚的领导者
2017/09/28 全球购物
选购国际女性时装设计师品牌:IFCHIC(支持中文)
2018/04/12 全球购物
心得体会范文
2014/01/04 职场文书
关于期中考试的反思
2014/02/02 职场文书
计算机科学与技术专业求职信
2014/09/03 职场文书
2014迎接教师节演讲稿
2014/09/10 职场文书
2016年优秀共产党员先进事迹材料
2016/02/29 职场文书
sql server偶发出现死锁的解决方法
2022/04/10 SQL Server
SQL Server中的游标介绍
2022/05/20 SQL Server