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 相关文章推荐
在PWS上安装PHP4.0正式版
Oct 09 PHP
约瑟夫环问题的PHP实现 使用PHP数组内部指针操作函数
Oct 12 PHP
PHP原生模板引擎 最简单的模板引擎
Apr 25 PHP
phpword插件导出word文件时中文乱码问题处理方案
Aug 19 PHP
php判断当前用户已在别处登录的方法
Jan 06 PHP
php实现用于验证所有类型的信用卡类
Mar 24 PHP
PHP实现简单的新闻发布系统实例
Jul 28 PHP
php微信公众号开发(3)php实现简单微信文本通讯
Dec 15 PHP
PHP二维数组去重算法
Dec 17 PHP
php获取指定数量随机字符串的方法
Feb 06 PHP
解决php 处理 form 表单提交多个 name 属性值相同的 input 标签问题
May 11 PHP
php+ajax实现异步上传文件或图片功能
Jul 18 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 fsockopen解决办法 php实现多线程
2014/01/20 PHP
JS获取鼠标坐标的实例方法
2013/07/18 Javascript
JS比较两个时间大小的简单示例代码
2013/12/20 Javascript
使用JS取得焦点(focus)元素代码
2014/03/22 Javascript
javascript:void(0)点击登录没反应怎么解决
2015/11/13 Javascript
详解javascript跨浏览器事件处理程序
2016/03/27 Javascript
快速解决js动态改变dom元素属性后页面及时渲染的问题
2016/07/06 Javascript
Javascript highcharts 饼图显示数量和百分比实例代码
2016/12/06 Javascript
JavaScript 详解预编译原理
2017/01/22 Javascript
详解React 在服务端渲染的实现
2017/11/16 Javascript
vue几个常用跨域处理方式介绍
2018/02/07 Javascript
Layui组件Table绑定行点击事件和获取行数据的方法
2018/08/19 Javascript
bootstrap table表格插件之服务器端分页实例代码
2018/09/12 Javascript
javascript防抖函数debounce详解
2019/06/11 Javascript
layui form表单提交后实现自动刷新
2019/10/25 Javascript
基于JavaScript实现猜数字游戏代码实例
2020/07/30 Javascript
JavaScript实现图片放大预览效果
2020/11/02 Javascript
[44:30]完美世界DOTA2联赛PWL S2 GXR vs Magma 第一场 11.25
2020/11/26 DOTA
[37:45]完美世界DOTA2联赛PWL S3 LBZS vs Phoenix 第二场 12.09
2020/12/11 DOTA
进一步探究Python的装饰器的运用
2015/05/05 Python
Pythont特殊语法filter,map,reduce,apply使用方法
2016/02/27 Python
Python中Collections模块的Counter容器类使用教程
2016/05/31 Python
详解Python Matplot中文显示完美解决方案
2019/03/07 Python
Python字符串和正则表达式中的反斜杠('\')问题详解
2019/09/03 Python
Python Lambda函数使用总结详解
2019/12/11 Python
python 多进程队列数据处理详解
2019/12/23 Python
python安装后的目录在哪里
2020/06/21 Python
Win10下配置tensorflow-gpu的详细教程(无VS2015/2017)
2020/07/14 Python
沙龙级头发造型工具:FOXYBAE
2018/07/01 全球购物
德国专业木制品经销商:Holz-Direkt24
2019/12/26 全球购物
商得四方公司面试题(gid+)
2014/04/30 面试题
2015试用期转正工作总结
2014/12/12 职场文书
详解Vue的sync修饰符
2021/05/15 Vue.js
一文带你理解vue创建一个后台管理系统流程(Vue+Element)
2021/05/18 Vue.js
Javascript之datagrid查询详解
2021/09/15 Javascript
基于Android10渲染Surface的创建过程
2022/08/14 Java/Android