Можно использовать
TransformStream
.
Кода будет поменьше, чем если создавать "прокси"
ReadableStream
. Единственное отличие, что
TransformStream
"ленивый". Т.е. если закомментировать
await newResponse.blob();
onProgress
не будет вызываться, хотя данные были загружены.
Собственно вот онлайн демо.
const response = await fetch("https://fetch-progress.anthum.com/30kbps/images/sunrise-baseline.jpg");
total.value = parseInt(response.headers.get("content-length")) || 0;
function onProgress(chunk) {
loaded.value += chunk.length;
}
const ts = new TransformStream({
transform(chunk, controller) {
onProgress(chunk);
controller.enqueue(chunk);
},
});
response.body.pipeThrough(ts);
const newResponse = new Response(ts.readable);
const blob = await newResponse.blob();
Имей в виду, что
"content-length"
может отсутствовать.
А также данные могут быть сжаты, если есть заголовок
"content-encoding"
, который, к слову, по-умолчанию будет недоступен для кода при кросс-доменных запросах.
И при
"content-encoding"
в
"content-length"
значение всегда указано для сжатого респонса, а в коллбеке будут уже не сжатые данные. Т.е.
loaded
в таком случае будет больше значения
total
(
"content-length"
)
---
"Прокси"
ReadableStream
:
const reader = response.body.getReader();
const readableStream = new ReadableStream({
async start(controller) {
while (true) {
const {done, value} = await reader.read();
if (done) { break; }
onProgress(value);
controller.enqueue(value);
}
controller.close();
reader.releaseLock();
},
cancel() {
void reader.cancel();
}
});
const newResponse = new Response(readableStream);