今天這篇文章和大家聊一聊如何做到只請求資源的一部分,這裡需要用到幾個http頭——range、if-range、content-range、accept-range。
Range頭信息介紹
Range主要用來設置獲取數據的範圍,格式如下:
<code>Range: <unit>=<range-start>-<range-end>Range: <unit>=<range-start>-<range-end>, <range-start>-<range-end>, <range-start>-<range-end>/<range-start>/<range-end>/<range-start>/<range-end>/<range-start>/<unit>/<range-end>/<range-start>/<unit>/<code>
如: 獲取 0-10字節的數據和15到結尾的數據
<code>Range: bytes=0-10,15-/<code>
If-Range主要用來判斷是否滿足範圍請求的條件,舉個例子,假設昨天你用迅雷下載了一部電影但是沒有下載完,今天你要接著下載,當再次下載時客戶端就需要和服務器驗證這部電影的資源內容有沒有發生變化,If-Range在這裡就是做驗證使用的 。
Content-Range表示響應數據的內容範圍,語法格式如下:
<code>Content-Range: <unit> <range-start>-<range-end>/<size>Content-Range: <unit> <range-start>-<range-end>/*Content-Range: <unit> */<size>/<unit>/<range-end>/<range-start>/<unit>/<size>/<range-end>/<range-start>/<unit>/<code>
- <unit> 類型,一般來說是bytes;/<unit>
- <range-start> 區間的起始值;/<range-start>
- <range-end> 區間的結束值;/<range-end>
- <size> 整個文件的大小(如果大小未知則用 "*" 表示)/<size>
例如:
<code>Content-Range: bytes10-15/22/<code>
Accept-Ranges用於服務器響應,告訴瀏覽器是否支持Range,
語法:
<code>Accept-Ranges: bytesAccept-Ranges: none/<code>
- none不支持任何範圍請求單位,由於其等同於沒有返回此頭部,因此很少使用。不過一些瀏覽器,比如IE9,會依據該頭部去禁用或者移除下載管理器的暫停按鈕;
- bytes 一般情況
代碼實現
上面介紹了幾個頭信息的概念,下面我們用代碼實現一下,大概流程如下:
我們還是以中間件的方式去實現。
如圖2,我們通過range頭獲取請求的範圍信息,如果類型合法,我們還需要處理範圍數據,處理方式和處理url的query一樣,在處理的過程中我們需要對不合法的範圍進行糾正和過濾。如果類型不合法,我們就正常返回整個內容。
對於range範圍內重疊和相鄰的區域可以做一次合併,例如:
bytes=50-55,0-10,5-10,56-60,可以合併為[{start: 0, end: 10},{start: 50, end: 60}]。
如圖3,如果range範圍無效,我們返回狀態碼416,告訴客戶端range是無效的,不滿足要求。
如圖4,如果range範圍校驗也沒問題,我們還需要通過if-range提供的信息與etag或者Last-Modified做對比(對比二選一)。
- 如果if-range沒有值,可以認為是無條件的,返回true;
- 如果和etag有一致的地方,證明資源未變,返回true,可以繼續部分請求;
- 如果給的時間條件大於修改的時間,證明資源也未變,可以繼續部分請求(大於還是小於,時間的意義可以自己定);
如果不滿足if-range條件,繼續走正常返回資源的邏輯,如果滿足那就開始返回部分資源。
如圖5,設置了狀態為206,這是http標識部分內容返回的狀態,另外還設置了accept-range和content-type。此處我們使用stream對內容進行分片,這裡只返回了一段範圍的內容。
對於多段請求,也是可以實現的,如下:
需要把content-type設置成multipart/byteranges; boundary=分隔符,這樣的話就可以分片下載了。
總結
這篇文章主要介紹了range頭相關的使用方法,內容還是蠻多的。本文的代碼實現沒那麼全,主要講了一下原理及流程,小夥伴們如果需要使用可以再打磨一下。
閱讀更多 做前端的蝸牛 的文章