PHP 關于 文件上傳 下載 斷點續傳問題
讓PHP下載代碼支持斷點續傳 主要靠的 HTTP協議中header? Content-Range來實現
先來說說 HTTP的下載原理
對于HTTP協議,向服務器請求某個文件時,只要發送類似如下的請求即可:
GET /Path/FileName HTTP/1.0
Host:?
www.server.com:80
Accept: **表示接收任何類型的數據。User-Agent表示用戶代理,這個字段可有可無,但強烈建議加上,因為它是服務器統計、追蹤以及識別客戶端的依據。Connection字段中的close表示使用非持久連接。
關于HTTP協議更多的細節可以參考RFC2616(HTTP 1.1)。因為我只是想通過HTTP協議實現文件下載,所以也只看了一部分,并沒有看全。
如果服務器成功收到該請求,并且沒有出現任何錯誤,則會返回類似下面的數據:
HTTP/1.0 200 OK
Content-Length: 13057672
Content-Type: application/octet-stream
Last-Modified: Wed, 10 Oct 2005 00:56:34 GMT
Accept-Ranges: bytes
ETag: "2f38a6cac7cec51:160c"
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
Date: Wed, 16 Nov 2005 01:57:54 GMT
Connection: close
下面來解釋下這些返回信息的含義
第一行是協議名稱及版本號,空格后面會有一個三位數的數字,是HTTP協議的響應狀態碼,200表示成功,OK是對狀態碼的簡短文字描述。狀態碼共有5類:
1xx屬于通知類;
2xx屬于成功類;
3xx屬于重定向類;
4xx屬于客戶端錯誤類;
5xx屬于服務端錯誤類。
對 于狀態碼,相信大家對404應該很熟悉,如果向一個服務器請求一個不存在的文件,就會得到該錯誤,通常瀏覽器也會顯示類似“HTTP 404 - 未找到文件”這樣的錯誤。Content-Length字段是一個比較重要的字段,它標明了服務器返回數據的長度,這個長度是不包含HTTP頭長度的。換句話說,我們的請求中并沒有Range字段(后面會說到),表示我們請求的是整個文件,所以Content-Length就是整個文件的大小。其余各字段是一些關于文件和服務器的屬性信息。
以上就是通過HTTP協議實現文件下載的全過程。但還不能實現斷點續傳,而實際上斷點續傳的實現非常簡單,只要在請求中加一個Range字段就可以了。
假如一個文件有1000個字節,那么其范圍就是0-999,則:
Range: bytes=500-??????表示讀取該文件的500-999字節,共500字節。
Range: bytes=500-599???表示讀取該文件的500-599字節,共100字節。
Range還有其它幾種寫法,但上面這兩種是最常用的,對于斷點續傳也足矣了。如果HTTP請求中包含Range字段,那么服務器會返回206(Partial Content),同時HTTP頭中也會有一個相應的Content-Range字段,類似下面的格式:
Content-Range: bytes 500-999/1000
Content-Range字段說明服務器返回了文件的某個范圍及文件的總長度。這時Content-Length字段就不是整個文件的大小了,而是對應文件這個范圍的字節數,這一點一定要注意。
下面來看PHP對斷點續傳支持的演示:
先定義一個函數? getRange() 這個函數用來處理? header中 Range 具體數據的處理
1 /* * $file_size 文件大小 */ 2 ? function getRange( $file_size ){ 3 $range = isset ( $_SERVER [ ' HTTP_RANGE ' ]) ? $_SERVER [ ' HTTP_RANGE ' ] : null ; 4 if ( ! empty ( $range )){ 5 $range = preg_replace ( ' /[\s|,].*/ ' , '' , $range ); 6 $range = explode ( ' - ' , substr ( $range , 6 )); 7 if ( count ( $range ) < 2 ) { 8 $range [ 1 ] = $file_size ; 9 } 10 $range = array_combine ( array ( ' start ' , ' end ' ) , $range ); 11 if ( empty ( $range [ ' start ' ])) { 12 $range [ ' start ' ] = 0 ; 13 } 14 if ( ! isset ( $range [ ' end ' ]) || empty ( $range [ ' end ' ])) { 15 $range [ ' end ' ] = $file_size ; 16 } 17 return $range ; 18 } 19 return null ; 20 }
假設文件的地址為 $file_path
1 $speed = 512 ; // 此參數為下載最大速度 2 ? $pos = strrpos ( $file_path , " / " ); 3 ? $file_name = substr ( $file_path , $pos + 1 ); 4 $file_size = filesize ( $file_path ); 5 $ranges = getRange( $file_size ); 6 $fh = fopen ( $file_path , " rb " ); 7 header ( ' Cache-control: public ' ); 8 header ( ' Content-Type: application/octet-stream ' ); 9 header ( ' Content-Disposition: attachment; filename= ' . $file_name ); 10 if ( $ranges != null ) { 11 header ( ' HTTP/1.1 206 Partial Content ' ); 12 header ( ' Accept-Ranges: bytes ' ); 13 header ( sprintf ( ' Content-Length: %u ' , $ranges [ ' end ' ] - $ranges [ ' start ' ])); 14 header ( sprintf ( ' Content-Range: bytes %s-%s/%s ' , $ranges [ ' start ' ] , $ranges [ ' end ' ] , $file_size )); 15 fseek ( $fh , sprintf ( ' %u ' , $ranges [ ' start ' ])); 16 } else { 17 header ( " HTTP/1.1 200 OK " ); 18 header ( sprintf ( ' Content-Length: %s ' , $file_size )); 19 } 20 while ( ! feof ( $fh )) 21 { 22 echo fread ( $fh , round ( $speed * 1024 , 0 )); 23 ob_flush (); 24 sleep ( 1 ); 25 } 26 ( $fh != null ) && fclose ( $fh );
基本如此 就可以解決一般性文件的斷點續傳或者下載了
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
