大部分針對物理文件的請求都希望獲取整個文件的內容,區間請求則與之相反,它希望獲取某個文件部分區間的內容。區間請求可以通過多次請求來獲取某個較大文件的全部內容,並實現斷點續傳。如果同一個文件同時存放到多台服務器,就可以利用區間請求同時下載不同部分的內容。與條件請求一樣,區間請求也作為標准定義在HTTP規范之中。
HTTP區間請求
如果希望通過一個GET請求獲取目標資源的某個區間的內容,就需要將這個區間存放到一個名為Range的報頭中。雖然HTTP規范允許指定多個區間,但是StaticFileMiddleware中間件只支持單一區間。分區所采用的計量單位,HTTP規范並未做強制的規定,但是StaticFileMiddleware中間件支持的單位為Byte,也就是說,它是以字節為單位對文件內容進行分區的。
Range報頭攜帶的分區信息采用的格式為bytes={from}-{to}({from}和{to}分別表示區間開始與結束的位置),如bytes=1000-1999表示獲取目標資源從1001到2000共計1000字節(第1個字節的位置為0)。如果{to}大於整個資源的長度,這樣的區間依然被認為是有效的,它表示從{from}到資源的最后一個字節。如果區間被定義成bytes={from}-這種形式,同樣表示區間從{from}到資源的最后一個字節。采用bytes=-{n}格式定義的區間則表示資源的最后n個字節。無論采用何種形式,如果{from}大於整個資源的總長度,這樣的區間定義就被視為不合法。
如果請求的Range報頭攜帶一個不合法的區間,服務端就會返回一個狀態碼為“416 Range Not Satisfiable”的響應,否則返回一個狀態碼為“206 Partial Content”的響應,響應的主體將只包含指定區間的內容。返回的內容在整個資源的位置通過響應報頭Content-Range來表示,采用的格式為{from}-{to}/{length}。除此之外,還有一個與區間請求相關的響應報頭Accept-Ranges,它表示服務端能夠接受的區間類型。例如,前面針對條件請求的響應都具有一個Accept-Ranges: bytes報頭,表示服務支持針對資源的區間划分。如果該報頭的值被設置為none,則意味着服務端不支持區間請求。
區間請求在某些時候也會驗證資源內容是否發生改變。在這種情況下,請求會利用一個名為If-Range的報頭攜帶一個時間戳或者整個資源(不是當前請求的區間)的標簽。服務端在接收到請求之后會根據這個報頭判斷請求的整個資源是否發生變化,如果判斷已經發生變化,它會返回一個狀態碼為“200 OK”的響應,響應主體將包含整個資源的內容。只有在判斷資源並未發生變化的前提下,服務端才會返回指定區間的內容。
針對靜態文件的區間請求
下面從HTTP請求和響應報文的角度來探討StaticFileMiddleware中間件針對區間請求的支持。我們依然沿用前面演示條件請求的實例,該實例中作為目標文件的foobar.txt包含26個字母和10個數字,加上UTF文本文件初始的3個字符(EF、BB、BF),所以總長度為39。我們發送如下兩個請求分別獲取前面26個字母(3-28)和后面10個數字(-10)。
GET http://localhost:50000/foobar.txt HTTP/1.1
Host: localhost:50000
Range: bytes=3-28
HTTP/1.1 206 Partial Content
Date: Wed, 18 Sep 2019 23:38:59 GMT
Content-Type: text/plain
Server: Kestrel
Content-Length: 26
Content-Range: bytes 3-28/39
Last-Modified: Wed, 18 Sep 2019 23:15:14 GMT
Accept-Ranges: bytes
ETag: "1d56e76ed13ed27"
abcdefghijklmnopqrstuvwxyz
GET http://localhost:50000/foobar.txt HTTP/1.1
Host: localhost:50000
Range: bytes=-10
HTTP/1.1 206 Partial Content
Date: Wed, 18 Sep 2019 23:39:51 GMT
Content-Type: text/plain
Server: Kestrel
Content-Length: 10
Content-Range: bytes 29-38/39
Last-Modified: Wed, 18 Sep 2019 23:15:14 GMT
Accept-Ranges: bytes
ETag: "1d56e76ed13ed27"
0123456789
由於請求中指定了正確的區間,所以我們會得到兩個狀態碼為“206 Partial Content”的響應,響應的主體僅包含目標區間的內容。除此之外,響應報頭Content-Range(“bytes 3-28/39”和“bytes 29-38/39”)指明了返回內容的區間范圍和整個文件的總長度。目標文件最后修改的時間戳和標簽同樣會存在於響應報頭Last-Modified與ETag之中。
接下來我們發送如下所示的一個區間請求,並刻意指定一個不合法的區間(50-)。正如HTTP規范所描述的那樣,在這種情況下可以得到一個狀態碼為“416 Range Not Satisfiable”的響應。
GET http://localhost:5000/foobar.txt HTTP/1.1
Host: localhost:5000
Range: bytes=50-
HTTP/1.1 416 Range Not Satisfiable
Date: Wed, 18 Sep 2019 23:43:21 GMT
Server: Kestrel
Content-Length: 0
Content-Range: bytes */39
GET http://localhost:5000/foobar.txt HTTP/1.1
Range: bytes=-10
If-Range: Wed, 18 Sep 2019 01:01:01 GMT
Host: localhost:5000
HTTP/1.1 200 OK
Date: Wed, 18 Sep 2019 23:45:32 GMT
Content-Type: text/plain
Server: Kestrel
Content-Length: 39
Last-Modified: Wed, 18 Sep 2019 23:15:14 GMT
Accept-Ranges: bytes
ETag: "1d56e76ed13ed27"
abcdefghijklmnopqrstuvwxyz0123456789
GET http://localhost:50000/foobar.txt HTTP/1.1
User-Agent: Fiddler
Host: localhost:50000
Range: bytes=-10
If-Range: "123abc456"
HTTP/1.1 200 OK
Date: Wed, 18 Sep 2019 23:46:36 GMT
Content-Type: text/plain
Server: Kestrel
Content-Length: 39
Last-Modified: Wed, 18 Sep 2019 23:15:14 GMT
Accept-Ranges: bytes
ETag: "1d56e76ed13ed27"
abcdefghijklmnopqrstuvwxyz0123456789
靜態文件中間件[1]: 搭建文件服務器
靜態文件中間件[2]: 條件請求以提升性能
靜態文件中間件[3]: 區間請求以提供部分內容
靜態文件中間件[4]: StaticFileMiddleware
靜態文件中間件[5]: DirectoryBrowserMiddleware & DefaultFilesMiddleware