You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现不加载全部文件块到浏览器内存的分块下载?

解决方案:流式分块下载,避免内存耗尽

核心思路是利用浏览器的ReadableStream API逐块处理文件数据,无需将全部内容加载到内存中再触发下载。具体实现如下:

1. 流式分块处理逻辑

创建可读流,在流的读取周期内逐次请求文件块,解码Base64数据后推送到流中,直到所有块读取完成。

完整代码实现

async function streamDownloadFile(fileHandle, chunkSize, fileName) {
  let isDone = false;

  // 构建可读流,逐块拉取数据
  const stream = new ReadableStream({
    async pull(controller) {
      if (isDone) {
        controller.close();
        return;
      }

      try {
        // 组装XML请求体
        const requestXml = `
<Request>
  <Function>cloud::readFile</Function>
  <ArgList>
    <Argument name="fileHandle">${fileHandle}</Argument>
    <Argument name="size">${chunkSize}</Argument>
  </ArgList>
</Request>`;

        // 发送分块请求
        const response = await fetch('/your-api-endpoint', {
          method: 'POST',
          body: requestXml,
          headers: { 'Content-Type': 'text/xml' }
        });

        if (!response.ok) throw new Error(`请求失败:${response.status}`);

        // 解析XML响应
        const responseXml = await response.text();
        const xmlDoc = new DOMParser().parseFromString(responseXml, 'text/xml');
        
        const bufferBase64 = xmlDoc.querySelector('Result[name="buffer"]').textContent;
        const status = xmlDoc.querySelector('Result[name="status"]').textContent;
        const bytesRead = parseInt(xmlDoc.querySelector('Result[name="size"]').textContent);

        // 判断是否读取完成
        if (status !== 'success' || bytesRead === 0) {
          isDone = true;
          controller.close();
          return;
        }

        // Base64解码为二进制数组
        const binaryStr = atob(bufferBase64);
        const uint8Array = new Uint8Array(binaryStr.length);
        for (let i = 0; i < binaryStr.length; i++) {
          uint8Array[i] = binaryStr.charCodeAt(i);
        }

        // 将当前块推入流
        controller.enqueue(uint8Array);
      } catch (err) {
        controller.error(err);
      }
    }
  });

  // 将流转换为可下载的Blob对象
  const blob = await new Response(stream).blob();
  const downloadUrl = URL.createObjectURL(blob);

  // 触发浏览器下载
  const a = document.createElement('a');
  a.href = downloadUrl;
  a.download = fileName;
  a.click();

  // 下载完成后释放资源
  setTimeout(() => URL.revokeObjectURL(downloadUrl), 1000);
}

2. 调用示例

// 按1MB分块下载文件
streamDownloadFile('your-open-file-handle', 1024 * 1024, 'target-file-name.ext');

关键注意事项

  • 分块顺序保证:确保cloud::readFile接口是按连续偏移量返回数据的,如果接口需要显式指定偏移参数,需在请求的ArgList中添加<Argument name="offset">${currentOffset}</Argument>并维护偏移量变量。
  • 资源清理:必须调用URL.revokeObjectURL释放临时URL,避免内存泄漏。
  • 错误扩展:可根据业务需求添加重试机制、下载中断逻辑或进度提示。

方案优势

  • 内存占用始终控制在单个分块的大小(如1MB),不会因为大文件累积内存导致浏览器崩溃。
  • 浏览器直接将流数据写入下载缓存,无需在内存中存储完整文件内容。

内容的提问来源于stack exchange,提问作者Mohamed Rifkan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 06:11:16