fetch 无法直接监听下载进度,需通过 response.body 获取 readablestream,用 getreader() 手动读取或 pipeto() 配合 transformstream 累计字节数计算进度;无 content-length 时仅能显示不确定进度。

Fetch 本身不直接支持下载进度监听,但可以通过 Response.body 获取可读流(ReadableStream),配合 ReadableStreamDefaultReader 或 stream.pipeTo() 逐步读取数据,并在每次读取时累加已接收字节数,从而计算下载进度。
用 ReadableStream 手动读取并计算进度
这是最常用、兼容性较好的方式。核心思路是:调用 fetch 获取响应,不直接调用 response.blob()(会阻塞直到全部下载完),而是用 response.body.getReader() 获取流读取器,循环 read(),累计 chunk.length,再结合 response.headers.get('content-length') 计算百分比。
- 先检查
Content-Length是否存在,否则无法预估总大小(如分块传输编码或动态生成文件) - 使用
Uint8Array接收每段数据,避免字符串转换开销 - 把所有 chunk 拼成一个
Uint8Array,最后用new Blob([chunks])构造文件 - 进度更新建议节流(如每 50ms 最多触发一次),避免频繁 setState 或 DOM 操作
用 stream.pipeTo() + TransformStream 实现边读边写
适合大文件或需要边下载边保存(如写入 IndexedDB、File System Access API)的场景。通过 TransformStream 在管道中插入进度统计逻辑:
- 创建
new TransformStream({ transform(chunk, controller) { ... } }) - 在
transform中累加chunk.byteLength,并调用controller.enqueue(chunk)向下游传递 - 将
response.body管道到该 TransformStream,再管道到WritableStream(如fileHandle.createWritable()) - 注意:
pipeTo是异步且不可中断,错误需监听abort或error事件
触发浏览器下载并显示进度条
下载完成后仍需触发浏览器默认下载行为。不能直接用 window.location 或 <a download></a>,因为 Blob URL 需要完整数据:
- 等所有 chunk 读取完毕,构造
new Blob(chunks, { type: response.headers.get('content-type') || 'application/octet-stream' }) - 用
URL.createObjectURL(blob)创建临时 URL - 创建隐藏
<a></a>标签,设置href和download属性,然后a.click() - 记得在下载完成后调用
URL.revokeObjectURL(url)释放内存
处理无 Content-Length 的情况
服务器未返回 Content-Length(常见于流式响应、代理转发、某些后端框架)时,无法计算精确百分比:
- 只能显示“正在下载…”或使用不确定进度条(如
<progress indeterminate></progress>) - 可通过
response.headers.get('content-disposition')提取建议文件名,提升用户体验 - 若服务端支持,改用
Range请求 + 分片下载,自行管理总大小和进度 - 考虑降级方案:对小文件直接用
response.blob(),大文件才走流式读取
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











