javascript中用fetch配合readablestream可实现流式下载,避免内存溢出;核心是直接读取response.body流,分块获取uint8array,支持边下边写blob、零拷贝转发至cache或service worker,并能实时计算进度和中断控制。

JavaScript 中用 fetch 配合 ReadableStream 处理下载,核心是避免将整个响应体一次性加载进内存,尤其适合大文件或流式内容(如视频、日志、CSV 导出等)。关键在于不调用 response.blob() 或 response.arrayBuffer(),而是直接读取 response.body —— 它是一个 ReadableStream。
获取流并分块读取
response.body 是可读流,需用 getReader() 获取读取器,再循环调用 reader.read() 拿到 Uint8Array 块。每次读取返回一个 Promise,解析后含 { done, value }:
- done 为 true 表示流已结束;
-
value 是当前数据块(
Uint8Array),为null时也代表结束; - 注意:必须手动处理
value,比如写入Blob、拼接 ArrayBuffer,或直接传给WritableStream(如 Service Worker 缓存)。
边下边写入 Blob 并触发下载
适用于浏览器端“渐进式下载”:收到一块就追加进 Blob,全部完成后再生成 URL 下载。注意不能在循环中反复创建新 Blob,应累积 Uint8Array 数组,最后一次性构造:
const chunks = [];
const reader = response.body.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
}
const blob = new Blob(chunks, { type: response.headers.get('content-type') || 'application/octet-stream' });
const url = URL.createObjectURL(blob);
const a = Object.assign(document.createElement('a'), { href: url, download: 'file.bin' });
document.body.append(a);
a.click();
URL.revokeObjectURL(url);
配合 WritableStream 实现零拷贝转发(如 PWA 离线缓存)
若需把响应流直接存入 Cache API 或 IndexedDB,可用 TransformStream 或 WritableStream 接收数据,避免中间数组累积:
- Cache API 不支持直接存流,但可用
new Response(stream, { headers })包装后 put 进 cache; - IndexedDB 需转成
ArrayBuffer或分块存为多个Uint8Array; - Service Worker 中常见模式:
event.respondWith(new Response(response.body, response))直接透传流,不消耗主线程内存。
处理进度与中断控制
流式下载天然支持进度计算:每读一块,累加 value.length 即当前已接收字节数。结合 response.headers.get('content-length') 可算百分比。中断控制靠 reader.cancel() 或 abortController.abort():
- 发起 fetch 时传入
{ signal: abortController.signal }; - 任意时刻调
abortController.abort(),会终止请求并让后续reader.read()拒绝; - 注意:已发出的 chunk 仍有效,cancel() 可清理未读完的流,防止内存泄漏。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











