企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
# 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下载类,支持断点续传 * 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; } } ?> ~~~ ## 使用方法如下 ~~~ <?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` ### 未开启断点续传的方式下载 ~~~ [root@luo_centos6.7 /usr/local/nginx/html/upload] # wget -c -O test.tar.gz http://192.168.2.222/upload/index.php --2016-01-08 19:05:04-- http://192.168.2.222/upload/index.php 正在连接 192.168.2.222:80... 已连接。 已发出 HTTP 请求,正在等待回应... 200 OK 长度:104857600 (100M) [application/octet-stream] 正在保存至: “test.tar.gz” 46% [=======================================> ] 48,755,480 227M/s [root@luo_centos6.7 /usr/local/nginx/html/upload] # wget -c -O test.tar.gz http://192.168.2.222/upload/index.php --2016-01-08 19:05:08-- http://192.168.2.222/upload/index.php 正在连接 192.168.2.222:80... 已连接。 已发出 HTTP 请求,正在等待回应... 200 OK 长度:104857600 (100M) [application/octet-stream] 正在保存至: “test.tar.gz” 100%[======================================================================================>] 104,857,600 52.1M/s in 1.9s 2016-01-08 19:05:10 (52.1 MB/s) - 已保存 “test.tar.gz” [104857600/104857600]) ~~~ **可以看到,wget -c不能断点续传** ### 开启断点续传 ~~~ [root@luo_centos6.7 /usr/local/nginx/html/upload] # wget -c -O test2.tar.gz http://192.168.2.222/upload/index2.php --2016-01-08 19:06:00-- http://192.168.2.222/upload/index2.php 正在连接 192.168.2.222:80... 已连接。 已发出 HTTP 请求,正在等待回应... 200 OK 长度:104857600 (100M) [application/octet-stream] 正在保存至: “test2.tar.gz” 36% [===============================> ] 38,757,893 9.90M/s eta(英国中部时间) 6s [root@luo_centos6.7 /usr/local/nginx/html/upload] # wget -c -O test2.tar.gz http://192.168.2.222/upload/index2.php --2016-01-08 19:06:07-- http://192.168.2.222/upload/index2.php 正在连接 192.168.2.222:80... 已连接。 已发出 HTTP 请求,正在等待回应... 206 Partial Content 长度:104857601 (100M),66099708 (63M) 字节剩余 [application/octet-stream] 正在保存至: “test2.tar.gz” 100%[++++++++++++++++++++++++++++++++======================================================>] 104,857,601 52.3M/s in 1.2s 2016-01-08 19:06:08 (52.3 MB/s) - 已保存 “test2.tar.gz” [104857601/104857601]) ~~~ 可以看到会从断点的位置(36%)开始下载。 # 拓展 | header头 | 释义 | | -- | -- | | header("Content-type:text/html;charset=utf-8") | 在服务器响应浏览器的请求时,告诉浏览器以编码格式为UTF-8的编码显示该内容 | | Header("Content-type: application/octet-stream") | 通过这句代码客户端浏览器就能知道服务端返回的文件形式 | | Header("Accept-Ranges: bytes") | 告诉客户端浏览器返回的文件大小是按照字节进行计算的 | | Header("Accept-Length:".$file_size) | 告诉浏览器返回的文件大小 | | Header("Content-Disposition: attachment; filename=".$file_name) | 告诉浏览器返回的文件的名称 | 以上四个Header()是必需的 fclose($fp)可以把缓冲区内最后剩余的数据输出到磁盘文件中,并释放文件指针和有关的缓冲区 [TOC]