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

Angular 12 多超大文件打包ZIP下载及进度展示正确实现问询

原有方案问题根因
  • 内存占用过高:所有文件二进制内容全部暂存在浏览器内存中,11GB级别的数据远超普通浏览器内存分配上限,直接导致页面卡顿甚至崩溃
  • 进度统计逻辑缺陷:只有所有文件全部拉取完成后才会触发JSZip的打包进度回调,拉取文件的数分钟到数十分钟阶段完全没有进度反馈,用户看到的进度一直为0%
  • 打包逻辑不合理:JSZip本身设计不支持超大文件流式处理,必须全量加载所有文件后才开始打包,天生不适用于GB级别的文件打包场景
方案1:服务端生成ZIP(优先推荐,生产环境唯一可用方案)

该方案完全规避前端内存占用问题,进度展示准确,稳定性最高:

  • 前端将需要打包的文件ID/URL列表提交给后端接口
  • 后端直接拉取所有资源,边拉取边写入ZIP输出流,不需要在服务端暂存全量ZIP文件
  • 前端直接接收ZIP流走浏览器原生下载逻辑,自动展示下载进度,全程不占用前端内存
  • 如果你的资源存储在阿里云OSS、腾讯云COS等云存储服务中,可以直接调用云厂商提供的批量打包ZIP接口,无需自己实现打包逻辑,性能更高、成本更低
方案2:前端流式打包下载(仅适用于服务端无法支持的小体量场景)

如果确实没有服务端支持的条件,使用流式打包方案替代JSZip,边拉取边打包边下载,无需全量加载文件到内存,同时可统计全链路进度:

依赖安装

选择对大文件支持更好的@zip.js/zip.js替代JSZip,配合流式下载工具:

npm install @zip.js/zip.js streamsaver

核心代码改造

DownloadService实现

import { ZipWriter, BlobReader, configure } from '@zip.js/zip.js';
import streamSaver from 'streamsaver';

// 配置zip.js使用Web Worker执行打包逻辑,避免阻塞主线程
configure({
  useWebWorkers: true,
  // 注意:将node_modules/@zip.js/zip.js/dist/z-worker.js放到你的Angular项目assets目录下,配置对应路径
  workerScripts: {
    deflate: ['/assets/z-worker.js']
  }
});

@Injectable({ providedIn: 'root' })
export class DownloadService {
  async downloadAll(
    resourceList: {url: string, filename: string, size: number}[],
    progressCallback?: (percent: number) => void
  ) {
    // 计算所有文件总大小,用于进度统计
    const totalSize = resourceList.reduce((sum, item) => sum + item.size, 0);
    let downloadedSize = 0;

    // 创建下载流,直接向用户输出ZIP文件,无需暂存全量数据
    const writeStream = streamSaver.createWriteStream('resources.zip');
    const zipWriter = new ZipWriter(writeStream);

    for (const resource of resourceList) {
      const response = await fetch(resource.url);
      if (!response.ok) throw new Error(`文件拉取失败:${resource.url}`);

      const reader = response.body.getReader();
      // 自定义流统计单文件下载进度
      const progressStream = new ReadableStream({
        async start(controller) {
          while (true) {
            const { done, value } = await reader.read();
            if (done) break;
            downloadedSize += value.length;
            const percent = Math.min(99.9, (downloadedSize / totalSize) * 100);
            progressCallback?.(percent);
            controller.enqueue(value);
          }
          controller.close();
        }
      });

      // 将文件流直接写入ZIP,无需加载全量文件到内存
      const fileBlob = await new Response(progressStream).blob();
      await zipWriter.add(resource.filename, new BlobReader(fileBlob));
    }

    // 完成ZIP打包,关闭流
    await zipWriter.close();
    progressCallback?.(100);
  }
}

组件调用改造

downloadAll() {
  // 注意:需要提前在资源元数据中存储每个文件的size字段,否则无法计算准确进度
  const resourceList = this.PROJECT.resources.map(u => ({
    url: u.link,
    filename: u.link.split('/').pop(),
    size: u.size
  }));

  this.downloadService.downloadAll(resourceList, (percent) => {
    console.log(`下载进度:${percent.toFixed(1)}%`);
    if (percent >= 100) {
      console.log('Zip Downloaded');
    }
  });
}

前端方案注意事项

  • 仅建议总大小不超过4GB的场景使用,超过4GB依然会有浏览器内存限制问题,必须走服务端方案
  • 需要资源存储服务配置CORS规则,且返回content-length响应头,否则无法正常拉取文件和统计进度
  • Web Worker配置必须正确,否则打包逻辑依然会阻塞主线程导致页面卡顿

内容的提问来源于stack exchange,提问作者Sergio Mendez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 23:15:04