前端使用File API下载大文件,此方式是否行得通?

在下载文件时如果下载的接口需要认证或添加一些自定义请求头,那么就不能使用a标签的方式下载;
在这个代码中使用了fetch,允许用户自定义参数下载,再调用浏览器的File API 获得写入磁盘的权限,然后读取响应流同时写入到硬盘,使文件流不用在内存中缓存(不占用内存);

实际的demo运行后发现并没有预想的那样,读取的同时写入到磁盘,而是下载完了才写入到磁盘。

请问代码上是否有逻辑错误,它为什么没有在读取时写入到磁盘?

代码地址

阅读 844
avatarAI BotBETA

问题分析:
你的代码没有实现真正的流式写入,核心原因是使用了过时的 FileSaver.js + Blob 方案,导致数据仍需完整加载到内存。即使使用较新的 File API,也需要配合 Streams API 实现分块写入。

关键问题点:

  1. Response.blob() 会缓冲所有数据
    当前代码中使用的 response.blob() 会强制等待所有网络数据加载到内存后才会生成 Blob 对象,这直接导致内存占用的核心问题。
  2. FileSaver.js 的工作机制
    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);
  }
}

技术原理说明:

  1. response.body 是 ReadableStream 对象,可直接访问原始字节流
  2. pipeTo() 方法会自动处理背压控制,实现动态速率传输
  3. 数据会以分块(chunk)形式从网络直接写入磁盘,无需内存缓存

兼容性注意:

  • 需要 Chrome 86+ 或 Edge 89+ 等现代浏览器
  • 要求页面运行在 HTTPS 安全上下文
  • 旧版浏览器可通过 web-streams-polyfill 实现降级

替代方案:
如果无法使用 File System Access API,可通过 Service Worker 实现伪流式下载,但仍无法完全避免内存占用。

1 个回答

设计如此。

REF: https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFi...

Any changes made through the stream won't be reflected in the file represented by the file handle until the stream has been closed. This is typically implemented by writing data to a temporary file, and only replacing the file represented by file handle with the temporary file when the writable file stream is closed.

注意加粗部分。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏