斷點續傳原理
所謂斷點續傳php無法下載文件到本地,也就是要從文件已經下載的地方開始繼續下載。在以前版本的 HTTP 協議是不支持斷點的,HTTP/1.1 開始就支持了。一般斷點下載時才用到 Range 和 -Range 實體頭。
不使用斷點續傳
get?/down.zip?http/1.1
accept:?image/gif,?image/x-xbitmap,?image/jpeg,?image/pjpeg,?application/vnd.ms-
excel,?application/msword,?application/vnd.ms-powerpoint,?*/*
accept-language:?zh-cn
accept-encoding:?gzip,?deflate
user-agent:?mozilla/4.0?(compatible;?msie?5.01;?windows?nt?5.0)
connection:?keep-alive
服務器收到請求后,按要求尋找請求的文件,提取文件的信息,然后返回給瀏覽器,返回信息如下:
HTTP/1.1?200?Ok
content-length=106786028
accept-ranges=bytes
date=mon,?30?apr?2001?12:56:11?gmt
etag=w/"02ca57e173c11:95b"
content-type=application/octet-stream
server=microsoft-iis/5.0
last-modified=mon,?30?apr?2001?12:56:11?gmt
使用斷點續傳
GET?/down.zip?HTTP/1.0
User-Agent:?NetFox
RANGE:?bytes=2000070-
Accept:?text/html,?image/gif,?image/jpeg,?*;?q=.2,?*/*;?q=.2
多了這一行 Range: bytes=-
這一行的意思就是告訴服務器down.zip 這個文件從 字節開始傳,前面的字節不用傳了。
Range的完整格式是:
Range:?bytes=startOffset-targetOffset/sum??
[表示從startOffset讀取,一直讀取到targetOffset位置,讀取總數為sum直接]
Range:?bytes=startOffset-targetOffset??
[字節總數也可以去掉]
服務器收到這個請求以后,返回的信息如下:
HTTP/1.1?206?Partial?Content
content-length=106786028
content-range=bytes?2000070-106786027/106786028
date=mon,?30?apr?2001?12:55:20?gmt
etag=w/"02ca57e173c11:95b"
content-type=application/octet-stream
server=microsoft-iis/5.0
last-modified=mon,?30?apr?2001?12:55:20?gmt
和前面服務器返回的信息比較一下,就會發現增加了一行:
Content-Range=bytes?2000070-106786027/106786028
返回的代碼也改為206了php無法下載文件到本地,而不再是200了。
HTTP/1.1?206?Partial?Content
以上原理你都搞懂了,那就可以進行斷點續傳的編程了。
用PHP實現斷點續傳的類
/**??php下載類,支持斷點續傳
?*???download:?下載文件
?*???setSpeed:?設置下載速度
?*???getRange:?獲取header中Range
?*/
class?FileDownload{
????/**?下載
?????*?@param?String??$file???要下載的文件路徑
?????*?@param?String??$name???文件名稱,為空則與下載的文件名稱一樣
?????*?@param?boolean?$reload?是否開啟斷點續傳
?????*/
????public?function?download($file,?$name='',?$reload=false){
????????$fp?=?@fopen($file,?'rb');
????????if($fp){
????????????if($name==''){
????????????????$name?=?basename($file);
????????????}
????????????$header_array?=?get_headers($file,?true);
????????????//var_dump($header_array);die;
????????????//?下載本地文件,獲取文件大小
????????????if?(!$header_array)?{
????????????????$file_size?=?filesize($file);
????????????}?else?{
????????????????$file_size?=?$header_array['Content-Length'];
????????????}
????????????$ranges?=?$this->getRange($file_size);
????????????$ua?=?$_SERVER["HTTP_USER_AGENT"];//判斷是什么類型瀏覽器
????????????header('cache-control:public');
????????????header('content-type:application/octet-stream');???
????????????$encoded_filename?=?urlencode($name);
????????????$encoded_filename?=?str_replace("+",?"%20",?$encoded_filename);
????????????//解決下載文件名亂碼
????????????if?(preg_match("/MSIE/",?$ua)?||??preg_match("/Trident/",?$ua)?){??????????????
????????????????header('Content-Disposition:?attachment;?filename="'?.$encoded_filename?.?'"');
????????????}?else?if?(preg_match("/Firefox/",?$ua))?{
????????????????header('Content-Disposition:?attachment;?filename*="utf8\'\''?.?$name?.?'"');
????????????}else?if?(preg_match("/Chrome/",?$ua))?{
????????????????header('Content-Disposition:?attachment;?filename="'?.?$encoded_filename?.?'"');
????????????}?else?{
????????????????header('Content-Disposition:?attachment;?filename="'?.?$name?.?'"');
????????????}
????????????//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));
????????????????//file_put_contents('test.log',sprintf('content-length:%u',$ranges['end']-$ranges['start']),FILE_APPEND);
????????????????//?fp指針跳到斷點位置
????????????????fseek($fp,?sprintf('%u',?$ranges['start']));
????????????}else{
????????????????file_put_contents('test.log','2222',FILE_APPEND);
????????????????header('HTTP/1.1?200?OK');
????????????????header('content-length:'.$file_size);
????????????}
????????????while(!feof($fp)){
????????????????//echo?fread($fp,?round($this->_speed*1024,0));
????????????????//echo?fread($fp,?$file_size);
????????????????echo?fread($fp,?4096);
????????????????ob_flush();
????????????}
????????????($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){
????????//file_put_contents('range.log',?json_encode($_SERVER),?FILE_APPEND);
????????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;
????}
}
$obj?=?new?FileDownload();
$obj->download('http://down.golaravel.com/laravel/laravel-master.zip','',?true);
以上是本文的全部內容,希望對你的學習有幫助,也感謝你對PHP自學中心的支持
你花了·來閱讀
點個
再走吧~