php实现大文件断点续传下载实例,看完你就知道超过100M以上的大文件如何断点传输了,这个功能还是比较经典实用的,毕竟大文件上传功能经常用得到。
1 require_once('download.class.php'); 2 date_default_timezone_set('Asia/Shanghai'); 3 error_reporting(E_STRICT); 4 5 function errorHandler($errno, $errstr, $errfile, $errline) { 6 echo '<p>error:', $errstr, '</p>'; 7 exit(); 8 } 9 10 set_error_handler('errorHandler'); 11 define('IS_DEBUG', true); 12 13 $filePath = 'test.zip'; 14 $mimeType = 'audio/x-matroska'; 15 $range = isset($_SERVER['HTTP_RANGE']) ? $_SERVER['HTTP_RANGE'] : null; 16 if (IS_DEBUG) { 17 // $range = "bytes=1000-1999\n2000"; 18 // $range = "bytes=1000-1999,2000"; 19 // $range = "bytes=1000-1999,-2000"; 20 // $range = "bytes=1000-1999,2000-2999"; 21 } 22 set_time_limit(0); 23 $transfer = new Transfer($filePath, $mimeType, $range); 24 if (IS_DEBUG) { 25 $transfer->setIsLog(true); 26 } 27 $transfer->send();
download.class.php
1 /** 2 * 文件传输,支持断点续传。 3 * 2g以上超大文件也有效 4 * @author MoXie 5 */ 6 class Transfer { 7 8 /** 9 * 缓冲单元 10 */ 11 const BUFF_SIZE = 5120; // 1024 * 5 12 13 /** 14 * 文件地址 15 * @var <String> 16 */ 17 18 private $filePath; 19 20 /** 21 * 文件大小 22 * @var <String> Php超大数字 字符串形式描述 23 */ 24 private $fileSize; 25 26 /** 27 * 文件类型 28 * @var <String> 29 */ 30 private $mimeType; 31 32 /** 33 * 请求区域(范围) 34 * @var <String> 35 */ 36 private $range; 37 38 /** 39 * 是否写入日志 40 * @var <Boolean> 41 */ 42 private $isLog = false; 43 44 /** 45 * 46 * @param <String> $filePath 文件路径 47 * @param <String> $mimeType 文件类型 48 * @param <String> $range 请求区域(范围) 49 */ 50 function __construct($filePath, $mimeType = null, $range = null) { 51 $this->filePath = $filePath; 52 $this->fileSize = sprintf('%u', filesize($filePath)); 53 $this->mimeType = ($mimeType != null) ? $mimeType : "application/octet-stream"; // bin 54 $this->range = trim($range); 55 } 56 57 /** 58 * 获取文件区域 59 * @return <Map> {'start':long,'end':long} or null 60 */ 61 private function getRange() { 62 /** 63 * Range: bytes=-128 64 * Range: bytes=-128 65 * Range: bytes=28-175,382-399,510-541,644-744,977-980 66 * Range: bytes=28-175\n380 67 * type 1 68 * RANGE: bytes=1000-9999 69 * RANGE: bytes=2000-9999 70 * type 2 71 * RANGE: bytes=1000-1999 72 * RANGE: bytes=2000-2999 73 * RANGE: bytes=3000-3999 74 */ 75 if (!empty($this->range)) { 76 $range = preg_replace('/[\s|,].*/', '', $this->range); 77 $range = explode('-', substr($range, 6)); 78 if (count($range) < 2) { 79 $range[1] = $this->fileSize; // Range: bytes=-100 80 } 81 $range = array_combine(array('start', 'end'), $range); 82 if (empty($range['start'])) { 83 $range['start'] = 0; 84 } 85 if (!isset($range['end']) || empty($range['end'])) { 86 $range['end'] = $this->fileSize; 87 } 88 return $range; 89 } 90 return null; 91 } 92 93 /** 94 * 向客户端发送文件 95 */ 96 public function send() { 97 $fileHande = fopen($this->filePath, 'rb'); 98 if ($fileHande) { 99 // setting 100 ob_end_clean(); // clean cache 101 ob_start(); 102 ini_set('output_buffering', 'Off'); 103 ini_set('zlib.output_compression', 'Off'); 104 $magicQuotes = get_magic_quotes_gpc(); 105 // set_magic_quotes_runtime(0); 106 // init 107 $lastModified = gmdate('D, d M Y H:i:s', filemtime($this->filePath)) . ' GMT'; 108 $etag = sprintf('w/"%s:%s"', md5($lastModified), $this->fileSize); 109 $ranges = $this->getRange(); 110 // headers 111 header(sprintf('Last-Modified: %s', $lastModified)); 112 header(sprintf('ETag: %s', $etag)); 113 header(sprintf('Content-Type: %s', $this->mimeType)); 114 $disposition = 'attachment'; 115 if (strpos($this->mimeType, 'image/') !== FALSE) { 116 $disposition = 'inline'; 117 } 118 header(sprintf('Content-Disposition: %s; filename="%s"', $disposition, basename($this->filePath))); 119 120 if ($ranges != null) { 121 if ($this->isLog) { 122 $this->log(json_encode($ranges) . ' ' . $_SERVER['HTTP_RANGE']); 123 } 124 header('HTTP/1.1 206 Partial Content'); 125 header('Accept-Ranges: bytes'); 126 header(sprintf('Content-Length: %u', $ranges['end'] - $ranges['start'])); 127 header(sprintf('Content-Range: bytes %s-%s/%s', $ranges['start'], $ranges['end'], $this->fileSize)); 128 // 129 fseek($fileHande, sprintf('%u', $ranges['start'])); 130 } else { 131 header("HTTP/1.1 200 OK"); 132 header(sprintf('Content-Length: %s', $this->fileSize)); 133 } 134 // read file 135 $lastSize = 0; 136 while (!feof($fileHande) && !connection_aborted()) { 137 $lastSize = sprintf("%u", bcsub($this->fileSize, sprintf("%u", ftell($fileHande)))); 138 if (bccomp($lastSize, self::BUFF_SIZE) > 0) { 139 $lastSize = self::BUFF_SIZE; 140 } 141 echo fread($fileHande, $lastSize); 142 ob_flush(); 143 flush(); 144 } 145 set_magic_quotes_runtime($magicQuotes); 146 ob_end_flush(); 147 } 148 if ($fileHande != null) { 149 fclose($fileHande); 150 } 151 } 152 153 /** 154 * 设置记录 155 * @param <Boolean> $isLog 是否记录 156 */ 157 public function setIsLog($isLog = true) { 158 $this->isLog = $isLog; 159 } 160 161 /** 162 * 记录 163 * @param <String> $msg 记录信息 164 */ 165 private function log($msg) { 166 try { 167 $handle = fopen('transfer_log.txt', 'a'); 168 fwrite($handle, sprintf('%s : %s' . PHP_EOL, date('Y-m-d H:i:s'), $msg)); 169 fclose($handle); 170 } catch (Exception $e) { 171 // null; 172 } 173 } 174 175 }
参考文章:http://blog.ncmem.com/wordpress/2023/09/20/php%e5%ae%9e%e7%8e%b0%e5%a4%a7%e6%96%87%e4%bb%b6%e6%96%ad%e7%82%b9%e7%bb%ad%e4%bc%a0%e4%b8%8b%e8%bd%bd%e5%ae%9e%e4%be%8b/
欢迎入群一起讨论