W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
?fetch
? 方法允許去跟蹤 下載 進(jìn)度。
請(qǐng)注意:到目前為止,fetch
方法無(wú)法跟蹤 上傳 進(jìn)度。對(duì)于這個(gè)目的,請(qǐng)使用 XMLHttpRequest,我們?cè)诤竺嬲鹿?jié)會(huì)講到。
要跟蹤下載進(jìn)度,我們可以使用 response.body
屬性。它是 ReadableStream
—— 一個(gè)特殊的對(duì)象,它可以逐塊(chunk)提供 body。在 Streams API 規(guī)范中有對(duì) ReadableStream
的詳細(xì)描述。
與 response.text()
,response.json()
和其他方法不同,response.body
給予了對(duì)進(jìn)度讀取的完全控制,我們可以隨時(shí)計(jì)算下載了多少。
這是從 response.body
讀取 response 的示例代碼:
// 代替 response.json() 以及其他方法
const reader = response.body.getReader();
// 在 body 下載時(shí),一直為無(wú)限循環(huán)
while(true) {
// 當(dāng)最后一塊下載完成時(shí),done 值為 true
// value 是塊字節(jié)的 Uint8Array
const {done, value} = await reader.read();
if (done) {
break;
}
console.log(`Received ${value.length} bytes`)
}
await reader.read()
調(diào)用的結(jié)果是一個(gè)具有兩個(gè)屬性的對(duì)象:
done
? —— 當(dāng)讀取完成時(shí)為 ?true
?,否則為 ?false
?。value
? —— 字節(jié)的類型化數(shù)組:?Uint8Array
?。請(qǐng)注意:
Streams API 還描述了如果使用
for await..of
循環(huán)異步迭代ReadableStream
,但是目前為止,它還未得到很好的支持(參見(jiàn) 瀏覽器問(wèn)題),所以我們使用了while
循環(huán)。
我們?cè)谘h(huán)中接收響應(yīng)塊(response chunk),直到加載完成,也就是:直到 done
為 true
。
要將進(jìn)度打印出來(lái),我們只需要將每個(gè)接收到的片段 value
的長(zhǎng)度(length)加到 counter 即可。
這是獲取響應(yīng),并在控制臺(tái)中記錄進(jìn)度的完整工作示例,下面有更多說(shuō)明:
// Step 1:?jiǎn)?dòng) fetch,并獲得一個(gè) reader
let response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits?per_page=100');
const reader = response.body.getReader();
// Step 2:獲得總長(zhǎng)度(length)
const contentLength = +response.headers.get('Content-Length');
// Step 3:讀取數(shù)據(jù)
let receivedLength = 0; // 當(dāng)前接收到了這么多字節(jié)
let chunks = []; // 接收到的二進(jìn)制塊的數(shù)組(包括 body)
while(true) {
const {done, value} = await reader.read();
if (done) {
break;
}
chunks.push(value);
receivedLength += value.length;
console.log(`Received ${receivedLength} of ${contentLength}`)
}
// Step 4:將塊連接到單個(gè) Uint8Array
let chunksAll = new Uint8Array(receivedLength); // (4.1)
let position = 0;
for(let chunk of chunks) {
chunksAll.set(chunk, position); // (4.2)
position += chunk.length;
}
// Step 5:解碼成字符串
let result = new TextDecoder("utf-8").decode(chunksAll);
// 我們完成啦!
let commits = JSON.parse(result);
alert(commits[0].author.login);
讓我們一步步解釋下這個(gè)過(guò)程:
fetch
,但不是調(diào)用 response.json()
,而是獲得了一個(gè)流讀取器(stream reader)response.body.getReader()
。請(qǐng)注意,我們不能同時(shí)使用這兩種方法來(lái)讀取相同的響應(yīng)。要么使用流讀取器,要么使用 reponse 方法來(lái)獲取結(jié)果。
Content-Length
header 中得到完整的響應(yīng)長(zhǎng)度。跨源請(qǐng)求中可能不存在這個(gè) header(請(qǐng)參見(jiàn) Fetch:跨源請(qǐng)求),并且從技術(shù)上講,服務(wù)器可以不設(shè)置它。但是通常情況下它都會(huì)在那里。
await reader.read()
,直到它完成。我們將響應(yīng)塊收集到數(shù)組 chunks
中。這很重要,因?yàn)樵谑褂猛辏╟onsumed)響應(yīng)后,我們將無(wú)法使用 response.json()
或者其他方式(你可以試試,將會(huì)出現(xiàn) error)去“重新讀取”它。
chunks
—— 一個(gè) Uint8Array
字節(jié)塊數(shù)組。我們需要將這些塊合并成一個(gè)結(jié)果。但不幸的是,沒(méi)有單個(gè)方法可以將它們串聯(lián)起來(lái),所以這里需要一些代碼來(lái)實(shí)現(xiàn):chunksAll = new Uint8Array(receivedLength)
? —— 一個(gè)具有所有數(shù)據(jù)塊合并后的長(zhǎng)度的同類型數(shù)組。.set(chunk, position)
? 方法,從數(shù)組中一個(gè)個(gè)地復(fù)制這些 ?chunk
?。chunksAll
中。但它是一個(gè)字節(jié)數(shù)組,不是字符串。要?jiǎng)?chuàng)建一個(gè)字符串,我們需要解析這些字節(jié)??梢允褂脙?nèi)建的 TextDecoder 對(duì)象完成。然后,我們可以 JSON.parse
它,如果有必要的話。
如果我們需要的是二進(jìn)制內(nèi)容而不是字符串呢?這更簡(jiǎn)單。用下面這行代碼替換掉第 4 和第 5 步,這行代碼從所有塊創(chuàng)建一個(gè) Blob
:
let blob = new Blob(chunks);
最后,我們得到了結(jié)果(以字符串或 blob 的形式表示,什么方便就用什么),并在過(guò)程中對(duì)進(jìn)度進(jìn)行了跟蹤。
再?gòu)?qiáng)調(diào)一遍,這不能用于 上傳 過(guò)程(現(xiàn)在無(wú)法通過(guò) fetch
獲取),僅用于 下載 過(guò)程。
另外,如果大小未知,我們應(yīng)該檢查循環(huán)中的 receivedLength
,一旦達(dá)到一定的限制就將其中斷。這樣 chunks
就不會(huì)溢出內(nèi)存了。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: