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 相关文章推荐
IIS php环境配置PHP5 MySQL5 ZendOptimizer phpmyadmin安装与配置
Nov 18 PHP
10个可以简化php开发过程的MySQL工具
Apr 11 PHP
php数组函数序列之rsort() - 对数组的元素值进行降序排序
Nov 02 PHP
thinkphp的c方法使用示例
Feb 24 PHP
PHP调用.NET的WebService 简单实例
Mar 27 PHP
PHP实现通过URL提取根域名
Mar 31 PHP
Yii基于CActiveForm的Ajax数据验证用法示例
Jul 14 PHP
浅谈PHP的exec()函数无返回值排查方法(必看)
Mar 31 PHP
thinkphp下MySQL数据库读写分离代码剖析
Apr 18 PHP
PHP空值检测函数与方法汇总
Nov 19 PHP
PHP实现一个轻量级容器的方法
Jan 28 PHP
PHP实现单条sql执行多个数据的insert语句方法
Oct 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
收音机史话 - 1960年代前后的DIY
2021/03/02 无线电
PHP用FTP类上传文件视频等的简单实现方法
2016/09/23 PHP
模仿JQuery.extend函数扩展自己对象的js代码
2009/12/09 Javascript
javascript XMLHttpRequest对象全面剖析
2010/04/24 Javascript
JavaScript之自定义类型
2012/05/04 Javascript
Javascript遍历Html Table示例(包括内容和属性值)
2014/07/08 Javascript
鼠标事件的screenY,pageY,clientY,layerY,offsetY属性详解
2015/03/12 Javascript
JavaScript处理解析JSON数据过程详解
2015/09/11 Javascript
JavaScript希尔排序、快速排序、归并排序算法
2016/05/08 Javascript
Jquery获取第一个子元素简单实例
2016/06/02 Javascript
JS实现上传图片实时预览功能
2017/05/22 Javascript
JS对象序列化成json数据和json数据转化为JS对象的代码
2017/08/23 Javascript
深入理解Vue 的钩子函数
2018/09/05 Javascript
Phaser.js实现简单的跑酷游戏附源码下载
2018/10/26 Javascript
Vue多组件仓库开发与发布详解
2019/02/28 Javascript
laypage.js分页插件使用方法详解
2019/07/27 Javascript
详解Nuxt.js中使用Element-UI填坑
2019/09/06 Javascript
Vue 使用beforeEach实现登录状态检查功能
2019/10/31 Javascript
vue列表数据发生变化指令没有更新问题及解决方法
2020/01/16 Javascript
深入理解 ES6中的 Reflect用法
2020/07/18 Javascript
JavaScript实现简易计算器小功能
2020/10/22 Javascript
python的paramiko模块实现远程控制和传输示例
2017/10/13 Python
pyspark操作MongoDB的方法步骤
2019/01/04 Python
Python绘制热力图示例
2019/09/27 Python
tensorflow:指定gpu 限制使用量百分比,设置最小使用量的实现
2020/02/06 Python
关于Python字符串显示u...的解决方式
2020/03/06 Python
pyinstaller将含有多个py文件的python程序做成exe
2020/04/29 Python
HTML5实现一个能够移动的小坦克示例代码
2013/09/02 HTML / CSS
时尚圣经:The Fashion Bible
2019/03/03 全球购物
分别介绍一下Session Bean和Entity Bean
2015/03/13 面试题
个人工作总结范文2014
2014/11/07 职场文书
2014年学校工作总结
2014/11/20 职场文书
2015年项目经理工作总结
2015/04/30 职场文书
期中考试后的感想
2015/08/07 职场文书
关于CentOS 8 搭建MongoDB4.4分片集群的问题
2021/10/24 MongoDB
Redis实现短信验证码登录的示例代码
2022/06/14 Redis