在下载文件时如果下载的接口需要认证或添加一些自定义请求头,那么就不能使用a标签的方式下载;
在这个代码中使用了fetch,允许用户自定义参数下载,再调用浏览器的File API 获得写入磁盘的权限,然后读取响应流同时写入到硬盘,使文件流不用在内存中缓存(不占用内存);
实际的demo运行后发现并没有预想的那样,读取的同时写入到磁盘,而是下载完了才写入到磁盘。
请问代码上是否有逻辑错误,它为什么没有在读取时写入到磁盘?
在下载文件时如果下载的接口需要认证或添加一些自定义请求头,那么就不能使用a标签的方式下载;
在这个代码中使用了fetch,允许用户自定义参数下载,再调用浏览器的File API 获得写入磁盘的权限,然后读取响应流同时写入到硬盘,使文件流不用在内存中缓存(不占用内存);
实际的demo运行后发现并没有预想的那样,读取的同时写入到磁盘,而是下载完了才写入到磁盘。
请问代码上是否有逻辑错误,它为什么没有在读取时写入到磁盘?
问题分析:
你的代码没有实现真正的流式写入,核心原因是使用了过时的 FileSaver.js + Blob 方案,导致数据仍需完整加载到内存。即使使用较新的 File API,也需要配合 Streams API 实现分块写入。
关键问题点:
response.blob()
会强制等待所有网络数据加载到内存后才会生成 Blob 对象,这直接导致内存占用的核心问题。saveAs(blob)
本质上是一次性将整个 Blob 写入磁盘,无法实现边下载边写入的流式效果。正确解决方案:
需要使用现代浏览器的 File System Access API + Streams API 实现真正的流式下载:
async function downloadFile() {
try {
// 请求文件流
const response = await fetch(url, {
headers: { Authorization: 'Bearer xxx' }
});
// 获取写入句柄
const fileHandle = await window.showSaveFilePicker();
const writableStream = await fileHandle.createWritable();
// 通过管道流式传输
await response.body.pipeTo(writableStream);
console.log('下载完成');
} catch (err) {
console.error('下载失败:', err);
}
}
技术原理说明:
response.body
是 ReadableStream 对象,可直接访问原始字节流pipeTo()
方法会自动处理背压控制,实现动态速率传输兼容性注意:
替代方案:
如果无法使用 File System Access API,可通过 Service Worker 实现伪流式下载,但仍无法完全避免内存占用。
10 回答11k 阅读
6 回答2.9k 阅读
5 回答4.7k 阅读✓ 已解决
4 回答3k 阅读✓ 已解决
2 回答2.5k 阅读✓ 已解决
3 回答5k 阅读✓ 已解决
3 回答1.8k 阅读✓ 已解决
设计如此。
注意加粗部分。