PHP 实现断点续传的原理和方法

0. http协议从1.1开始支持静态获取文件的部分内容,为多线程下载和断点续传提供了技术支持。它通过在Header里两个参数实现的,客户端发请求时对应的是Accept-Range ,服务器端响应时对应的是 Content-Range。

1. 具体的请求和响应格式是这样: 资料来源 http://www.liqwei.com/network/protocol/2011/886.shtml

Range

用于请求头中,指定第一个字节的位置和最后一个字节的位置,一般格式:

Range:(unit=first byte pos)-[last byte pos]

Content-Range

用于响应头,指定整个实体中的一部分的插入位置,他也指示了整个实体的长度。在服务器向客户返回一个部分响应,它必须描述响应覆盖的范围和整个实体长度。一般格式:

Content-Range: bytes (unit first byte pos) - [last byte pos]/[entity legth]

请求下载整个文件:

    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:文件总大小

2. 问题及应对

有些虚拟机配置时它是不支持断点传输的,下载时不显示总大小和进度条,只能看到下载了多少,甚至是一直处于 0 ,直到下载完成,这在下载文件时很不爽;此外,服务器对 apk 文件类型没有指明是 Content-type: application/octet-stream ,造成火狐浏览器对安卓应用 apk 以文本方式输入,这是不是更不方便。

基于此,有必要通过 PHP 对这种特殊的情况作处理,方法是浏览器给这个 PHP 脚本发送 GET 请求下载文件,PHP 脚本给浏览器响应的 header 中包含 Content-Type: application/octet-stream 和 Content-Range: bytes 0-800/801 即可。同时须用 flush() 函数刷新浏览器的cache ,就能看到下载了多少,否则下载大小一直处于 0

3. 实例

既然思路有了,具体的代码或许很简单。我们只需处理服务端,无需理会客户端发了什么请求

<?php

set_time_limit(0);                                                 // 这个必须有
$filename = $_GET['appid'];                                        // 接受的GET请求参数,其值为服务器上的文件
if(file_exists($filename)) die("该文件不存在");                    // 如果文件不存在,则终止脚本
$fileinfo = pathinfo($filename);                                   // 分析请求的值
$size = filesize($filename);                                       // 计算文件的大小
$size2 = $size-1;

header('Content-Range: bytes 0-' . $size2 . '/' . $size);          // 返回内容范围,断点续传
header('Content-Type: application/octet-stream');                  // 文件类型强制为二进制
#header('Content-Type: application/x-'.$fileinfo['extension']);    // 或许这样也可以
header('Content-Type: multipart/byteranges');                      // 多段传输
header('Content-Length: '. $size);                                 // 文件的总大小
// 提示用户保存,当 application/octet-stream 被使用时,任何类型的文件均被下载,不被浏览器打开。
header('Content-Disposition: attachment; filename='.$fileinfo['basename']);

flush();                          // 刷新浏览器的cache
readfile($filename);              // 读取文件
exit(0);

4. 现成的函数  资料来源 http://www.cnblogs.com/lq527/p/6228577.html

修正了  header('Content-Type: application/octet-stream');

header('Accenpt-Ranges: bytes');

Content-Dispositon

<?php
/**
 * PHP-HTTP断点续传实现
 * @param string $path: 文件所在路径
 * @param string $file: 文件名
 * @return void
 */
function download($path,$file) {
  $real = $path.'/'.$file;
  if(!file_exists($real)) {
    return false;
  }
  $size = filesize($real);
  $size2 = $size-1;
  $range = 0;
  if(isset($_SERVER['HTTP_RANGE'])) {   //http_range表示请求一个实体/文件的一个部分,用这个实现多线程下载和断点续传!
    header('HTTP /1.1 206 Partial Content');
    $range = str_replace('=','-',$_SERVER['HTTP_RANGE']);
    $range = explode('-',$range);
    $range = trim($range[1]);
    header('Content-Length:'.$size);
    header('Content-Range: bytes '.$range.'-'.$size2.'/'.$size);
  } else {
    header('Content-Length:'.$size);
    header('Content-Range: bytes 0-'.$size2.'/'.$size);
  }
  header('Accenpt-Ranges: bytes');
  header('Content-Type: application/octet-stream');
  header("Cache-control: public");
  header("Pragma: public");
  //解决在IE中下载时中文乱码问题
  $ua = $_SERVER['HTTP_USER_AGENT'];
  if(preg_match('/MSIE/',$ua)) {    //表示正在使用 Internet Explorer。
    $ie_filename = str_replace('+','%20',urlencode($file));
    header('Content-Disposition:attachment; filename='.$ie_filename);
  } else {
    header('Content-Disposition:attachment; filename='.$file);
  }
  $fp = fopen($real,'rb+');
  fseek($fp,$range);                //fseek:在打开的文件中定位,该函数把文件指针从当前位置向前或向后移动到新的位置,新位置从文件头开始以字节数度量。成功则返回 0;否则返回 -1。注意,移动到 EOF 之后的位置不会产生错误。
  while(!feof($fp)) {               //feof:检测是否已到达文件末尾 (eof)
    set_time_limit(0);              //注释①
    print(fread($fp,1024));         //读取文件(可安全用于二进制文件,第二个参数:规定要读取的最大字节数)
    ob_flush();                     //刷新PHP自身的缓冲区
     flush();                       //刷新缓冲区的内容(严格来讲, 这个只有在PHP做为apache的Module(handler或者filter)安装的时候, 才有实际作用. 它是刷新WebServer(可以认为特指apache)的缓冲区.)
  }
  fclose($fp);
}

5. Apache2 开启断点续传

在httpd.conf中尝试mod_headers: Header set Accept-Ranges bytes

#开启Accept-Ranges响应头

<IfModule mod_headers.c>
 Header set Accept-Ranges bytes
</IfModule>

Lighttpd用户尝试在lighttpd.conf中进行下面的配置:

## enabled for all file types
##server.range-requests = "enable"
## But, disable it for pdf files
##$HTTP["url"] =~ "\.pdf$" {  server.range-requests = "disable"}

6. 资料参考

http断点续传原理:http头 Range、Content-Range   http://www.liqwei.com/network/protocol/2011/886.shtml

HTTP Header里的Range和Content-Range参数         http://www.tuicool.com/articles/RBrMnen

php支持断点续传的文件下载类                     http://www.tuicool.com/articles/eMr2Ufq

PHP实现HTTP断点续传的方法                       http://www.cnblogs.com/lq527/p/6228577.html

HTTP状态码详解                                  http://tool.oschina.net/commons?type=5

PHP 实现断点续传的原理和方法的更多相关文章

  1. .net断点续传的原理

    在了解HTTP断点续传的原理之前,先来说说HTTP协议,HTTP协议是一种基于tcp的简单协议,分为请求和回复两种.请求协议是由客户机(浏览器)向服务器(WEB SERVER)提交请求时发送报文的协议 ...

  2. fMRI数据分析处理原理及方法(转载)

    原文地址:http://www.cnblogs.com/minks/p/4889497.html 近年来,血氧水平依赖性磁共振脑功能成像(Blood oxygenation level-depende ...

  3. fMRI数据分析处理原理及方法

    来源: 整理文件的时候翻到的,来源已经找不到了囧感觉写得还是不错,贴在这里保存. 近年来,血氧水平依赖性磁共振脑功能成像(Blood oxygenation level-dependent funct ...

  4. 客户端HTTP断点续传的原理

    其实断点续传的原理很简单,就是在Http的请求上和一般的下载有所不同而已. 打个比方,浏览器请求服务器上的一个文时,所发出的请求如下:假设服务器域名为wwww.scu.edu.cn,文件名为down. ...

  5. Atitit.实现继承的原理and方法java javascript .net c# php ...

    Atitit.实现继承的原理and方法java javascript .net c# php ... 1. 实现继承的问题 1 2. 如何拷贝基类方法?采用prototype原型方式,通过冒充对象 1 ...

  6. Oracle中的SQL分页查询原理和方法详解

    Oracle中的SQL分页查询原理和方法详解 分析得不错! http://blog.csdn.net/anxpp/article/details/51534006

  7. 数据融合(data fusion)原理与方法

    数据融合(data fusion)原理与方法 数据融合(data fusion)最早被应用于军事领域.     现在数据融合的主要应用领域有:多源影像复合.机器人和智能仪器系统.战场和无人驾驶飞机.图 ...

  8. fMRI数据分析处理原理及方法————转自网络

    fMRI数据分析处理原理及方法 来源: 整理文件的时候翻到的,来源已经找不到了囧感觉写得还是不错,贴在这里保存. 近年来,血氧水平依赖性磁共振脑功能成像(Blood oxygenation level ...

  9. HTTP文件断点续传的原理

    前几天一个同事跑过来找我说,我们在广告素材视频这块想做断点续传,就是这次某个视频缓存到一半,下次不用重头开始,可以在原来停留得位置开始继续下载.以提供更好的用户体验. 同时说需要我们支持吐素材地址的业 ...

随机推荐

  1. iOS 全局修改UINavigation 后退按钮

    将导航栏的后退按钮中的文字去掉一直是老大难问题,现在可以使用运行时机制,将后退按钮文字清空 创建UINavigationItem的category,直接上代码 .h文件 #import <UIK ...

  2. 百度地图API的使用示例

    刚刚工作的时候写过百度地图API文档,那时候没有记录到技术博客里面,今天在群里看见有个姑娘在问这个问题,重温了一遍,这个API还算好用.百度地图API,集成简单好用,全面,兼容问题,文档全面: 官方文 ...

  3. Spring拦截器 /* 和 /** 的区别

    SpringMVC 拦截器拦截 /* 和 /** 的区别: /* : 匹配一级,即 /add , /query 等 /** : 匹配多级,即 /add , /add/user, /add/user/u ...

  4. WebKit载入流程 - 概述

    之前写了几篇载入流程的说明,是从下向上看,有点仅仅见树木不见森林的感觉.经过近期一段时间的学习,有了能加以概括抽象的方法. WebKit载入流程和页面组成是直接相关的,页面就是WebKit要载入的对象 ...

  5. easyui combobox 获取焦点

    easyui combobox 获取焦点 学习了:http://blog.csdn.net/foart/article/details/14446809 可以直接用: $('#spanZhudaoci ...

  6. 灵活的运用Model类

    1.定义接口 using System; using System.Collections.Generic; using System.Linq; using System.Web; namespac ...

  7. oracle job有定时执行功能

    oracle job有定时执行的功能,可以在指定的时间点或每天的某个时间点自行执行任务.   oracle job有定时执行的功能,可以在指定的时间点或每天的某个时间点自行执行任务. 一.查询系统中的 ...

  8. 5. webservice通信调用天气预报接口实例

    转自:https://blog.csdn.net/xiejuan6105/article/details/78452605 一:环境搭建 1:新建一个java project工程weatherInf ...

  9. Docker -- 2 -- 利用docker部署网站和数据库

    在Docker – 系统整洁之道 – 1中已经对Docker的一些命令和Docker镜像的使用及操作做了记录. 这次就利用docker进行一次真正的实例使用,使用docker搭建一个简单的答题系统,这 ...

  10. 网站及监控利器 Pandora FMS使用体验

    Pandora FMS 是一个 开源的应用程序 ,用来监测网站的各种活动,它可以收到实时的监测报告,并发送到你指定的邮箱,也可以通过e-mail, SMS 发送. 650) this.width=65 ...