当前位置:首页 > PHP教程 > php上传下载 > 列表

php实现断点续传大文件示例代码

发布:smiling 来源: PHP粉丝网  添加日期:2022-03-15 09:14:58 浏览: 评论:0 

这篇文章主要介绍了如何用php实现断点续传大文件,文中代码非常细致,帮助大家学习和参考,感兴趣的朋友可以了解下。

一、断点续传原理

所谓断点续传,也就是要从文件已经下载的地方开始继续下载,在以前版本的 HTTP 协议是不支持断点的,HTTP/1.1 开始就支持了,一般断点下载时才用到 Range 和 Content-Range 实体头。

不使用断点续传

  1. get /down.zip http/1.1 
  2. accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms- 
  3. excel, application/msword, application/vnd.ms-powerpoint, */* 
  4. accept-language: zh-cn 
  5. accept-encoding: gzip, deflate 
  6. user-agent: mozilla/4.0 (compatible; msie 5.01; windows nt 5.0) 
  7. connection: keep-alive 

服务器收到请求后,按要求寻找请求的文件,提取文件的信息,然后返回给浏览器,返回信息如下:

  1. HTTP/1.1 200 Ok 
  2. content-length=106786028 
  3. accept-ranges=bytes 
  4. date=mon, 30 apr 2001 12:56:11 gmt 
  5. etag=w/"02ca57e173c11:95b" 
  6. content-type=application/octet-stream 
  7. server=microsoft-iis/5.0 
  8. last-modified=mon, 30 apr 2001 12:56:11 gmt 

使用断点续传

  1. GET /down.zip HTTP/1.0 
  2. User-Agent: NetFox 
  3. RANGE: bytes=2000070- 
  4. Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2 

多了这么一行Range: bytes=2000070-

这一行的意思就是告诉服务器down.zip这个文件从2000070字节开始传,前面的字节不用传了。

Range的完整格式是:

Range: bytes=startOffset-targetOffset/sum [表示从startOffset读取,一直读取到targetOffset位置,读取总数为sum直接]

Range: bytes=startOffset-targetOffset [字节总数也可以去掉]

服务器收到这个请求以后,返回的信息如下:

  1. HTTP/1.1 206 Partial Content 
  2. content-length=106786028 
  3. content-range=bytes 2000070-106786027/106786028 
  4. date=mon, 30 apr 2001 12:55:20 gmt 
  5. etag=w/"02ca57e173c11:95b" 
  6. content-type=application/octet-stream 
  7. server=microsoft-iis/5.0 
  8. last-modified=mon, 30 apr 2001 12:55:20 gmt 

和前面服务器返回的信息比较一下,就会发现增加了一行:

Content-Range=bytes 2000070-106786027/106786028

返回的代码也改为206了,而不再是200了。

HTTP/1.1 206 Partial Content

知道了以上原理,就可以进行断点续传的编程了。

二、PHP实现

  1. /** php下载类,支持断点续传 
  2.  * download: 下载文件 
  3.  * setSpeed: 设置下载速度 
  4.  * getRange: 获取header中Range 
  5.  */ 
  6.    
  7. class FileDownload{ 
  8.    
  9.  /** 下载 
  10.  * @param String $file 要下载的文件路径 
  11.  * @param String $name 文件名称,为空则与下载的文件名称一样 
  12.  * @param boolean $reload 是否开启断点续传 
  13.  */ 
  14.  public function download($file$name=''$reload=false){ 
  15.  $fp = @fopen($file'rb'); 
  16.  if($fp){ 
  17.  if($name==''){ 
  18.  $name = basename($file); 
  19.  } 
  20.  $header_array = get_headers($file, true); 
  21.  //var_dump($header_array);die; 
  22.  // 下载本地文件,获取文件大小 
  23.  if (!$header_array) { 
  24.  $file_size = filesize($file); 
  25.  } else { 
  26.  $file_size = $header_array['Content-Length']; 
  27.  } 
  28.  $ranges = $this->getRange($file_size); 
  29.  $ua = $_SERVER["HTTP_USER_AGENT"];//判断是什么类型浏览器 
  30.  header('cache-control:public'); 
  31.  header('content-type:application/octet-stream');  
  32.    
  33.  $encoded_filename = urlencode($name); 
  34.  $encoded_filename = str_replace("+""%20"$encoded_filename); 
  35.    
  36.  //解决下载文件名乱码 
  37.  if (preg_match("/MSIE/"$ua) || preg_match("/Trident/"$ua) ){  
  38.  header('Content-Disposition: attachment; filename="' .$encoded_filename . '"'); 
  39.  } else if (preg_match("/Firefox/"$ua)) { 
  40.  header('Content-Disposition: attachment; filename*="utf8\'\'' . $name . '"'); 
  41.  }else if (preg_match("/Chrome/"$ua)) { 
  42.  header('Content-Disposition: attachment; filename="' . $encoded_filename . '"'); 
  43.  } else { 
  44.  header('Content-Disposition: attachment; filename="' . $name . '"'); 
  45.  } 
  46.  //header('Content-Disposition: attachment; filename="' . $name . '"'); 
  47.    
  48.  if($reload && $ranges!=null){ // 使用续传 
  49.  header('HTTP/1.1 206 Partial Content'); 
  50.  header('Accept-Ranges:bytes'); 
  51.    
  52.  // 剩余长度 
  53.  header(sprintf('content-length:%u',$ranges['end']-$ranges['start'])); 
  54.    
  55.  // range信息 
  56.  header(sprintf('content-range:bytes %s-%s/%s'$ranges['start'], $ranges['end'], $file_size)); 
  57.  //file_put_contents('test.log',sprintf('content-length:%u',$ranges['end']-$ranges['start']),FILE_APPEND); 
  58.  // fp指针跳到断点位置 
  59.  fseek($fp, sprintf('%u'$ranges['start'])); 
  60.  }else
  61.  file_put_contents('test.log','2222',FILE_APPEND); 
  62.  header('HTTP/1.1 200 OK'); 
  63.  header('content-length:'.$file_size); 
  64.  } 
  65.    
  66.  while(!feof($fp)){ 
  67.  //echo fread($fp, round($this->_speed*1024,0)); 
  68.  //echo fread($fp, $file_size); 
  69.  echo fread($fp, 4096); 
  70.  ob_flush(); 
  71.  } 
  72.    
  73.  ($fp!=null) && fclose($fp); 
  74.  }else
  75.  return ''
  76.  } 
  77.  } 
  78.    
  79.  /** 设置下载速度 
  80.  * @param int $speed 
  81.  */ 
  82.  public function setSpeed($speed){ 
  83.  if(is_numeric($speed) && $speed>16 && $speed<4096){ 
  84.  $this->_speed = $speed
  85.  } 
  86.  } 
  87.    
  88.  /** 获取header range信息 
  89.  * @param int $file_size 文件大小 
  90.  * @return Array 
  91.  */ 
  92.  private function getRange($file_size){ 
  93.  //file_put_contents('range.log', json_encode($_SERVER), FILE_APPEND); 
  94.  if(isset($_SERVER['HTTP_RANGE']) && !emptyempty($_SERVER['HTTP_RANGE'])){ 
  95.  $range = $_SERVER['HTTP_RANGE']; 
  96.  $range = preg_replace('/[\s|,].*/'''$range); 
  97.  $range = explode('-'substr($range, 6)); 
  98.  if(count($range)<2){ 
  99.  $range[1] = $file_size
  100.  } 
  101.  $range = array_combine(array('start','end'), $range); 
  102.  if(emptyempty($range['start'])){ 
  103.  $range['start'] = 0; 
  104.  } 
  105.  if(emptyempty($range['end'])){ 
  106.  $range['end'] = $file_size
  107.  } 
  108.  return $range
  109.  } 
  110.  return null; 
  111.  } 
  112.    
  113. $obj = new FileDownload(); 
  114. $obj->download('http://down.golaravel.com/laravel/laravel-master.zip','', true);

Tags: php断点续传

分享到: