Http系列:斷點續傳與多線程下載

前言

當下載電影時,我經常會想中斷下載後,爲何點擊開始時會在中斷的地方繼續下載呢?
又或者在看在線電影時,爲何能夠按着播放條拖動就能看到想看的片斷呢?node

http的range請求將解決以上困惑。git

多線程、斷點續傳、隨機點播等的場景的步驟

一、客戶端明確任務:從哪開始下載github

  • 本地是否已有部分文件:文件已下載部分在服務器端發生改變?
  • 使用幾個線程併發下載

二、下載文件的指定部份內容
三、下載完畢後拼裝成統一的文件bash

HTTP Range規範

在RFC7233中有詳細介紹
一、容許服務器基於客戶端的請求只發送響應包體的一部分給到客戶端,而客戶端自動將多個片斷的包體組合成完整的體積更大的包體。服務器

  • 支持斷點續傳
  • 支持多線程下載
  • 支持視頻播放器實時拖動

二、服務器經過Accept-Range頭部表示是否支持Range請求多線程

  • Accept-Ranges = acceptable-ranges
  • 例如:
    Accept-Ranges: bytes: 支持;
    Accept-Ranges: none: 不支持

Range請求範圍的單位

基於字節爲單位的時候,舉例:設置響應體長度爲10000併發

  • 第1個500字節:
    bytes=0-499 // 從0開始
  • 第2個500字節:
    bytes=500-999
    bytes=500-600, 601-999
    bytes=500-700, 601-999
  • 最後1個500字節
    bytes=-500
    bytes=9500-
  • 僅要第一個和最後一個字節:bytes=0-0, -1

經過Range頭部傳遞請求範圍,如:Range: bytes=0-499curl

測試

下面用一些小例子有測試一下。
用node搭了一個簡單的服務器,返回的數字是22個字節的響應體post

'Hello World 0123456789';
複製代碼

如今用curl命令獲取所有的響應體,而後訪問0-5的字節段: 測試

-H參數添加 HTTP 請求的標頭。
上面的命令就是添加HTTP頭Range: bytes=0-5。
返回的是Hello (加上空格)一共六個字節。

如今獲取第21個字節及之後的字節段,就能夠用20-:
返回的是89

Range條件請求

  • 若是客戶端已經獲得了Range響應的一部分,並想在這部分響應未過時的狀況下,獲取其餘部分的響應
    常與If-Unmodified-Since或者If-Match頭部共同使用
  • If-Range = entity-tag / HTTP-date
    可使用Etag或者Last-Modified

測試

下面用etag測試一下Range條件請求

首先獲取0-5字節段

而後用-I來看看生成Hello 時服務器生成Etag的值

接下來,用這個值放到If-Match中獲取6-10字節段:World

若是Etag發生了變化,來看看結果會怎麼樣,將最後的0改成1
返回412 Precondition Failed

結論

經過條件請求能夠判斷兩次下載之間,服務器端資源有沒有發生變化。若是發生了變化,就能夠經過412這個響應知道,資源已經發生了變化。

服務器響應

若是隻獲取部分的body,那麼服務器端返回的響應碼不是200,而是206。
206 Partial Content

  • Content-Range頭部:顯示當前片斷響應體在完整包體中的位置

  • Content-Range = byte-content-range / other-content-range
    { btye-content-range = bytes-unit SP (byte-range-resp / unsatisfied-range)
    byte-range-resp = byte-range '/' (complete-length / '')
    complete-length = 1
    DIGIT // 完整資源的大小,若是未知則用*號替代
    bytr-range = first-byte-pos "-" last-byte-pos }

  • 例如:
    一、Content-Range: bytes 42-1233/1234
    二、Content-Range: bytes 42-1233/*

測試

用一個視頻播放的例子來看看206響應的樣子。

416 Range Not Statisfiable

  • 請求範圍不知足實際資源的大小,其中Content-Range中的complete-length顯示完整響應的長度,例如
    Content-Range: bytes */1234

測試

若是獲取範圍超出實際資源的大小,好比獲取30-40。返回416

200 OK

  • 服務器不支持Range請求時,則以200返回完整的響應包體

多重範圍與multipart

  • 請求:
    Range: bytes=0-50, 100-150
  • 響應:
    Content-Type: multipart/byteranges; boundary=...

測試

獲取5-10, 10-15片斷。

總結

一、客戶端經過Range頭部傳遞請求範圍

二、服務端返回Accept-Range頭部表示是否支持Range請求。

三、客戶端若是在獲得Range響應的一部分,並想在這部分響應未過時的狀況下,獲取其餘部分的響應,能夠用If-Range頭部使用Etag或者Last-Modified爲值。

四、只獲取部分的body,服務器返回206響應碼,其中Content-Range頭部顯示當前片斷響應體在完整包體中的位置

五、客戶端想多重範圍下載資源,在Range頭部的格式爲Range: bytes=0-50, 100-150...(用逗號分隔)
響應頭部Content-Type: multipart/byteranges; boundary=...

系列文章

更多文章請移步樓主github,若是喜歡請點一下star,對做者也是一種鼓勵

相關文章
相關標籤/搜索