HTTP斷點續傳
Range:客戶端發請求的範圍
Content-Range:服務端返回當前請求範圍和文件總大小
續傳成功返回206
文件又變動,返回200和新文件內容
HTTP1.1 協議(RFC2616)開始支持獲取文件的部分內容,這爲並行下載以及斷點續傳提供了技術支持。它通過在 Header 裏兩個參數實現的,客戶端發請求時對應的是 Range ,服務器端響應時對應的是 Content-Range。
Range
用於請求頭中,指定第一個字節的位置和最後一個字節的位置,一般格式:
Range:(unit=first byte pos)-[last byte pos]
1.以下幾點需要注意:
(1)這個數據區間是個閉合區間,起始值是0,所以“Range: bytes=0-1”這樣一個請求實際上是在請求開頭的2個字節。
(2)“Range: bytes=-200”,它不是表示請求文件開始位置的201個字節,而是表示要請求文件結尾處的200個字節。
(3)如果last byte pos小於first byte pos,那麼這個Range請求就是無效請求,server需要忽略這個Range請求,然後迴應一個200,把整個文件發給client。
(4)如果last byte pos大於等於文件長度,那麼這個Range請求被認爲是不能滿足的,server需要迴應一個416,Requested range not satisfiable。
2.示例解釋:
Range: bytes=0-499 表示第 0-499 字節範圍的內容
Range: bytes=500-999 表示第 500-999 字節範圍的內容
Range: bytes=-500 表示最後 500 字節的內容
Range: bytes=500- 表示從第 500 字節開始到文件結束部分的內容
Range: bytes=0-0,-1 表示第一個和最後一個字節
Range: bytes=500-600,601-999 同時指定幾個範圍
Content-Range
用於響應頭中,在發出帶 Range 的請求後,服務器會在 Content-Range 頭部返回當前接受的範圍和文件總大小。一般格式:
Content-Range: bytes (unit first byte pos) - [last byte pos]/[entity legth]
例如:
請求下載整個文件:
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:文件總大小
而在響應完成後,返回的響應頭內容也不同:
HTTP/1.1 200 Ok(不使用斷點續傳方式)
HTTP/1.1 206 Partial Content(使用斷點續傳方式)
總結:
HTTP1.1協議(RFC2616)中定義了斷點續傳相關的HTTP頭 Range和Content-Range字段,一個最簡單的斷點續傳實現大概如下:
1.客戶端下載一個1024K的文件,已經下載了其中512K
2. 網絡中斷,客戶端請求續傳,因此需要在HTTP頭中申明本次需要續傳的片段:
Range:bytes=512000-
這個頭通知服務端從文件的512K位置開始傳輸文件
3. 服務端收到斷點續傳請求,從文件的512K位置開始傳輸,並且在HTTP頭中增加:
Content-Range:bytes 512000-/1024000
並且此時服務端返回的HTTP狀態碼應該是206,而不是200。
但是在實際場景中,會出現一種情況,即在終端發起續傳請求時,URL對應的文件內容在服務端已經發生變化,此時續傳的數據肯定是錯誤的。如何解決這個問題了?顯然此時我們需要有一個標識文件唯一性的方法。在RFC2616中也有相應的定義,比如實現Last-Modified來標識文件的最後修改時間,這樣即可判斷出續傳文件時是否已經發生過改動。同時RFC2616中還定義有一個ETag的頭,可以使用ETag頭來放置文件的唯一標識,比如文件的MD5值。
終端在發起續傳請求時應該在HTTP頭中申明If-Match 或者If-Modified-Since 字段,幫助服務端判別文件變化。
另外RFC2616中同時定義有一個If-Range頭,終端如果在續傳是使用If-Range。If-Range中的內容可以爲最初收到的ETag頭或者是Last-Modfied中的最後修改時候。服務端在收到續傳請求時,通過If-Range中的內容進行校驗,校驗一致時返回206的續傳回應,不一致時服務端則返回200迴應,迴應的內容爲新的文件的全部數據。
===========================================================
多線程下載:
假設你要開發一個多線程下載工具,你會自然的想到把文件分割成多個部分,比如4個部分,然後創建4個線程,每個線程負責下載一個部分,如果文件大小爲403個byte,那麼你的分割方式可以爲:0-99 (前100個字節),100-199(第二個100字節),200-299(第三個100字節),300-402(最後103個字節)。
分割完成,每個線程都明白自己的任務,比如線程3的任務是負責下載200-299這部分文件,現在的問題是:線程3發送一個什麼樣的請求報文,才能夠保證只請求文件的200-299字節,而不會干擾其他線程的任務。這時,我們可以使用HTTP1.1的Range頭。Range頭域可以請求實體的一個或者多個子範圍,Range的值爲0表示第一個字節,也就是Range計算字節數是從0開始的:
表示頭500個字節:Range: bytes=0-499
表示第二個500字節:Range: bytes=500-999
表示最後500個字節:Range: bytes=-500
表示500字節以後的範圍:Range: bytes=500-
第一個和最後一個字節:Range: bytes=0-0,-1
同時指定幾個範圍:Range: bytes=500-600,601-999
所以,線程3發送的請求報文必須有這一行:
Range: bytes=200-299
服務器接收到線程3的請求報文,發現這是一個帶有Range頭的GET請求,如果一切正常,服務器的響應報文會有下面這行:
HTTP/1.1 206 OK
表示處理請求成功,響應報文還有這一行
Content-Range: bytes 200-299/403
斜槓後面的403表示文件的大小,通常Content-Range的用法爲:
. The first 500 bytes:
Content-Range: bytes 0-499/1234
. The second 500 bytes:
Content-Range: bytes 500-999/1234
. All except for the first 500 bytes:
Content-Range: bytes 500-1233/1234
. The last 500 bytes:
Content-Range: bytes 734-1233/1234