當(dāng)用戶從靜態(tài)文件服務(wù)器上獲取諸如歌曲這樣的流媒體文件時(shí),如果網(wǎng)絡(luò)連接斷開,重連后未做處理,就需要重新下載這個文件。所以為了避免這種情況,我們的服務(wù)器需要一種斷點(diǎn)續(xù)傳的功能。而http 1.1中正好規(guī)定了一種
Range機(jī)制,我們可以通過這種機(jī)制來進(jìn)行分片傳輸。
Range
當(dāng)一個服務(wù)器支持Range時(shí),客戶端可以將需要發(fā)送的內(nèi)容分成很多份發(fā)送給服務(wù)端,服務(wù)端可以每次接收部分內(nèi)容。有了這樣的能力,遇到斷網(wǎng)的情況,我們可以在客戶端記錄下已經(jīng)傳送的文件范圍,網(wǎng)絡(luò)恢復(fù)后再將剩余部分發(fā)送給服務(wù)端,這樣就實(shí)現(xiàn)了斷點(diǎn)續(xù)傳。
Range的在http 1.1中的具體定義可以參考文檔:
具體流程如下:
瀏覽器請求內(nèi)容。
-
服務(wù)器告訴瀏覽器,該內(nèi)容可以使用 Accept-Ranges 消息頭進(jìn)行分部分請求。
response.setHeader('Accept-Ranges', 'bytes'); -
瀏覽器重新發(fā)送請求,用 Range 消息頭告訴服務(wù)器需要的內(nèi)容范圍。
發(fā)送的Range格式:字節(jié)數(shù)(bytes)= (開始)-(結(jié)束)
這是瀏覽器告知服務(wù)器所需分部分內(nèi)容范圍的消息頭. 注意開始和結(jié)束位置是都包括在內(nèi)的,而且是從0開始的. 這個消息頭也可以不發(fā)送兩個位置,其含義如下:
- 如果結(jié)束位置被去掉了,服務(wù)器會返回從聲明的開始位置到整個內(nèi)容的結(jié)束位置內(nèi)容的最后一個可用字節(jié)。
- 如果開始位置被去掉了,結(jié)束位置參數(shù)可以被描述成從最后一個可用的字節(jié)算起可以被服務(wù)器返回的字節(jié)數(shù)。
而服務(wù)器會分如下兩種情況響應(yīng)瀏覽器的請求:
- 如果范圍是合理的,服務(wù)器會返回所請求的部分內(nèi)容,并帶上 206 Partial Content 狀態(tài)碼. 當(dāng)前內(nèi)容的范圍會在 Content-Range 消息頭中申明。
發(fā)送的Content-Range格式:字節(jié)數(shù)(bytes)=(開始)-(結(jié)束)/(總數(shù)) - 如果范圍是不可用的(例如,比內(nèi)容的總字節(jié)數(shù)大), 服務(wù)器會返回 416 請求范圍不合理 Requested Range Not Satisfiable 狀態(tài)碼. 可用的范圍也會在 Content-Range 消息頭中聲明。
nodejs簡單實(shí)現(xiàn)
getStream(req, res, filepath, statObj) {
let start = 0;
let end = statObj.size - 1;
let range = req.headers['range'];
if (range) {
res.setHeader('Accept-Range', 'bytes');
res.statusCode = 206;
let result = range.match(/bytes=(\d*)-(\d*)/);
if (result) {
start = isNaN(result[1]) ? start : parseInt(result[1]);
end = isNaN(result[2]) ? end : parseInt(result[2]) - 1;
}
}
return fs.createReadStream(filepath, {
start, end
});
}
待續(xù)
自我小結(jié)
在搭建node靜態(tài)服務(wù)器過程中學(xué)習(xí)利用Http1.1的Range來實(shí)現(xiàn)斷點(diǎn)續(xù)傳功能