php实现的支持断点续传的文件下载类


Posted in PHP onSeptember 23, 2014

本文实例讲述了php实现的支持断点续传的文件下载类及其用法,是非常实用的技巧。分享给大家供大家参考。具体方法如下:

通常来说,php支持断点续传,主要依靠HTTP协议中 header HTTP_RANGE实现。

HTTP断点续传原理:

Http头 Range、Content-Range()
HTTP头中一般断点下载时才用到Range和Content-Range实体头,
Range用户请求头中,指定第一个字节的位置和最后一个字节的位置,如(Range:200-300)
Content-Range用于响应头

请求下载整个文件:

GET /test.rar HTTP/1.1
Connection: close
Host: 116.1.219.219
Range: bytes=0-801 //一般请求下载整个文件是bytes=0- 或不用这个头

一般正常回应:

HTTP/1.1 200 OK
Content-Length: 801     
Content-Type: application/octet-stream
Content-Range: bytes 0-800/801 //801:文件总大小

FileDownload.class.php类文件代码如下:

<?php 
/** php下载类,支持断点续传 
*  Date:  2013-06-30 
*  Author: test 
*  Ver:  1.0 
* 
*  Func: 
*  download: 下载文件 
*  setSpeed: 设置下载速度 
*  getRange: 获取header中Range 
*/ 
 
class FileDownload{ // class start 
 
  private $_speed = 512;  // 下载速度 
 
  /** 下载 
  * @param String $file  要下载的文件路径 
  * @param String $name  文件名称,为空则与下载的文件名称一样 
  * @param boolean $reload 是否开启断点续传 
  */ 
  public function download($file, $name='', $reload=false){ 
    if(file_exists($file)){ 
      if($name==''){ 
        $name = basename($file); 
      } 
 
      $fp = fopen($file, 'rb'); 
      $file_size = filesize($file); 
      $ranges = $this->getRange($file_size); 
 
      header('cache-control:public'); 
      header('content-type:application/octet-stream'); 
      header('content-disposition:attachment; filename='.$name); 
 
      if($reload && $ranges!=null){ // 使用续传 
        header('HTTP/1.1 206 Partial Content'); 
        header('Accept-Ranges:bytes'); 
         
        // 剩余长度 
        header(sprintf('content-length:%u',$ranges['end']-$ranges['start'])); 
         
        // range信息 
        header(sprintf('content-range:bytes %s-%s/%s', $ranges['start'], $ranges['end'], $file_size)); 
         
        // fp指针跳到断点位置 
        fseek($fp, sprintf('%u', $ranges['start'])); 
      }else{ 
        header('HTTP/1.1 200 OK'); 
        header('content-length:'.$file_size); 
      } 
 
      while(!feof($fp)){ 
        echo fread($fp, round($this->_speed*1024,0)); 
        ob_flush(); 
        //sleep(1); // 用于测试,减慢下载速度 
      } 
 
      ($fp!=null) && fclose($fp); 
 
    }else{ 
      return ''; 
    } 
  } 
 
  /** 设置下载速度 
  * @param int $speed 
  */ 
  public function setSpeed($speed){ 
    if(is_numeric($speed) && $speed>16 && $speed<4096){ 
      $this->_speed = $speed; 
    } 
  } 
 
  /** 获取header range信息 
  * @param int  $file_size 文件大小 
  * @return Array 
  */ 
  private function getRange($file_size){ 
    if(isset($_SERVER['HTTP_RANGE']) && !empty($_SERVER['HTTP_RANGE'])){ 
      $range = $_SERVER['HTTP_RANGE']; 
      $range = preg_replace('/[\s|,].*/', '', $range); 
      $range = explode('-', substr($range, 6)); 
      if(count($range)<2){ 
        $range[1] = $file_size; 
      } 
      $range = array_combine(array('start','end'), $range); 
      if(empty($range['start'])){ 
        $range['start'] = 0; 
      } 
      if(empty($range['end'])){ 
        $range['end'] = $file_size; 
      } 
      return $range; 
    } 
    return null; 
  } 
} // class end 
 
?>

demo示例代码如下:

<?php 
require('FileDownload.class.php'); 
$file = 'book.zip'; 
$name = time().'.zip'; 
$obj = new FileDownload(); 
$flag = $obj->download($file, $name); 
//$flag = $obj->download($file, $name, true); // 断点续传 
 
if(!$flag){ 
  echo 'file not exists'; 
} 
?>

断点续传测试方法:

使用linux wget命令去测试下载, wget -c -O file http://xxx

1.先关闭断点续传

$flag = $obj->download($file, $name);
test@ubuntu:~/Downloads$ wget -O test.rar http://demo.test.com/demo.php 
--2013-06-30 16:52:44-- http://demo.test.com/demo.php 
正在解析主机 demo.test.com... 127.0.0.1 
正在连接 demo.test.com|127.0.0.1|:80... 已连接。 
已发出 HTTP 请求,正在等待回应... 200 OK 
长度: 10445120 (10.0M) [application/octet-stream] 
正在保存至: “test.rar” 
 
30% [============================>                                   ] 3,146,580  513K/s 估时 14s 
^C 
test@ubuntu:~/Downloads$ wget -c -O test.rar http://demo.test.com/demo.php 
--2013-06-30 16:52:57-- http://demo.test.com/demo.php 
正在解析主机 demo.test.com... 127.0.0.1 
正在连接 demo.test.com|127.0.0.1|:80... 已连接。 
已发出 HTTP 请求,正在等待回应... 200 OK 
长度: 10445120 (10.0M) [application/octet-stream] 
正在保存至: “test.rar” 
30% [============================>                                   ] 3,146,580  515K/s 估时 14s 
^C

可以看到,wget -c不能断点续传 

2.开启断点续传

$flag = $obj->download($file, $name, true);
test@ubuntu:~/Downloads$ wget -O test.rar http://demo.test.com/demo.php 
--2013-06-30 16:53:19-- http://demo.test.com/demo.php 
正在解析主机 demo.test.com... 127.0.0.1 
正在连接 demo.test.com|127.0.0.1|:80... 已连接。 
已发出 HTTP 请求,正在等待回应... 200 OK 
长度: 10445120 (10.0M) [application/octet-stream] 
正在保存至: “test.rar” 
 
20% [==================>                                        ] 2,097,720  516K/s 估时 16s 
^C 
test@ubuntu:~/Downloads$ wget -c -O test.rar http://demo.test.com/demo.php 
--2013-06-30 16:53:31-- http://demo.test.com/demo.php 
正在解析主机 demo.test.com... 127.0.0.1 
正在连接 demo.test.com|127.0.0.1|:80... 已连接。 
已发出 HTTP 请求,正在等待回应... 206 Partial Content 
长度: 10445121 (10.0M),7822971 (7.5M) 字节剩余 [application/octet-stream] 
正在保存至: “test.rar” 
 
100%[++++++++++++++++++++++++=========================================================================>] 10,445,121  543K/s  花时 14s   
 
2013-06-30 16:53:45 (543 KB/s) - 已保存 “test.rar” [10445121/10445121])

可以看到会从断点的位置(%20)开始下载。 

本文实例完整源码可点击此处本站下载。

相信本文所述对大家的PHP程序设计有一定的借鉴价值。

PHP 相关文章推荐
一个查看session内容的函数
Oct 09 PHP
php 数据库字段复用的基本原理与示例
Jul 22 PHP
php使用正则表达式提取字符串中尖括号、小括号、中括号、大括号中的字符串
Apr 05 PHP
php+js iframe实现上传头像界面无跳转
Apr 29 PHP
PHP判断IP并转跳到相应城市分站的方法
Mar 25 PHP
php生成静态html页面的方法(2种方法)
Sep 14 PHP
一个完整的php文件上传类实例讲解
Oct 27 PHP
Symfony2学习笔记之控制器用法详解
Mar 17 PHP
Yii2中设置与获取别名的函数(setAlias和getAlias)用法分析
Jul 25 PHP
php监测数据是否成功插入到Mysql数据库的方法
Nov 25 PHP
PHP PDO和消息队列的个人理解与应用实例分析
Nov 25 PHP
php-7.3.6 编译安装过程
Feb 11 PHP
php实现的click captcha点击验证码类实例
Sep 23 #PHP
PHP实现自动登入google play下载app report的方法
Sep 23 #PHP
PHP遍历文件夹与文件类及处理类用法实例
Sep 23 #PHP
PHP邮件发送类PHPMailer用法实例详解
Sep 22 #PHP
php实现的CSS更新类实例
Sep 22 #PHP
php的XML文件解释类应用实例
Sep 22 #PHP
php实现的返回数据格式化类实例
Sep 22 #PHP
You might like
图书管理程序(二)
2006/10/09 PHP
php中实现记住密码自动登录的代码
2011/03/02 PHP
ThinkPHP之A方法实例讲解
2014/06/20 PHP
php数组查找函数总结
2014/11/18 PHP
php实现有序数组打印或排序的方法【附Python、C及Go语言实现代码】
2016/11/10 PHP
Zend Framework基于Command命令行建立ZF项目的方法
2017/02/18 PHP
PHP获取二叉树镜像的方法
2018/01/17 PHP
php+redis消息队列实现抢购功能
2018/02/08 PHP
一份老外写的XMLHttpRequest代码多浏览器支持兼容性
2007/01/11 Javascript
Ajax一统天下之Dojo整合篇
2007/03/24 Javascript
容易被忽略的JS脚本特性
2011/09/13 Javascript
javascript轻松实现当鼠标移开时已弹出子菜单自动消失
2013/12/29 Javascript
javascript省市区三级联动下拉框菜单实例演示
2015/11/29 Javascript
jQuery实现注册会员时密码强度提示信息功能示例
2017/09/05 jQuery
在vue2.0中引用element-ui组件库的方法
2018/06/21 Javascript
在vue项目中正确使用iconfont的方法
2018/09/28 Javascript
vue slot与传参实例代码讲解
2019/04/28 Javascript
uni app仿微信顶部导航条功能
2019/09/17 Javascript
Sublime Text3 配置 NodeJs 环境的方法
2020/05/20 NodeJs
解决Vue中的生命周期beforeDestory不触发的问题
2020/07/21 Javascript
[01:13]2014DOTA2西雅图邀请赛 舌尖上的TI4
2014/07/08 DOTA
[03:01]完美世界DOTA2联赛PWL S2 集锦第二期
2020/12/03 DOTA
python机器学习之KNN分类算法
2018/08/29 Python
Python用61行代码实现图片像素化的示例代码
2018/12/10 Python
pandas 把数据写入txt文件每行固定写入一定数量的值方法
2018/12/28 Python
python 输出所有大小写字母的方法
2019/01/02 Python
详解Python列表赋值复制深拷贝及5种浅拷贝
2019/05/15 Python
Python中字符串List按照长度排序
2019/07/01 Python
python 日志 logging模块详细解析
2020/03/31 Python
python3中celery异步框架简单使用+守护进程方式启动
2021/01/20 Python
HTML5 CSS3给网站设计带来出色效果
2009/07/16 HTML / CSS
Nike挪威官网:Nike.com (NO)
2018/11/26 全球购物
2014年9.18纪念日演讲稿
2014/09/14 职场文书
安全责任书
2015/01/29 职场文书
文案策划岗位职责
2015/02/11 职场文书
springboot中一些比较常用的注解总结
2021/06/11 Java/Android