「使用JSZip」进行浏览器多文件下载并压缩ZIP

3,496 阅读2分钟

最近有个需求,需要实现多个文件批量压缩后批量下载。

我第一反应就是,在服务端进行压缩生成文件,生成新的URL返回前端,打开新窗口下载。

这样的坏处就是当文件过多时:

  1. 后端实现复杂。
  2. 服务器资源占用太高,功能模块访问过多或者文件太多导致服务器资源吃紧。
  3. 前端需要轮训等待后台打包完成,逻辑实现复杂。
  4. 客户直接关闭浏览器,服务端还在打包,造成浪费资源 ...

于是思考,是否可以在浏览器端进行ZIP压缩?
搜索 浏览器压缩 关键字 出来了一个看名称就知道是啥的插件 「JSZip」官方文档
思路逐渐清晰,开始...

使用Fetch api下载文件

/**
       *  使用fetch 下载单个文件
       * 返回 对象 包含 文件名称和二进制文件Blob对象
       *
       */
      const download = (url) =>  fetch(url).then((res) => ({
          name: url.substring(url.lastIndexOf("/") + 1, url.length),
          blob: res.blob(),
        }));

使用浏览器 Fetch 下载文件,返回「blob对象」以及处理好文件名称。

使用 Promise.all 实现多个文件下载

    const downLoadGroup = urls => Promise.all(urls.map(download))

当直接使用 promise.all 时,将会同时进行多个get请求,恰巧使用批量下载的人比较多,那将给服务器带来不小的压力。
把众多请求进行 分组,5个或10个下载分成一组,再进行Promise.all 请求。一组完成之后再进行另外组,将会极大的减少服务器压力。
使用 「bluebirdjs」 扩展了Promise 原型方法,就不用自己手动去做这些操作,这里是使用Promise.map 这个方法进行分组的。

      /**
       * 使用bluebird 扩展方法 扩展Promise
       * 优化GET请求
       */
      const downloadGroup = (urls, group_size) => {
        return Promise.map(urls, async (url) => await download(url), {
          concurrency: group_size,
        });
      };

使用 JSZip进行文件压缩

    /**
       * 使用JSZip压缩 文件
       * 使用blob模式
       * 使用 FileSaver 下载
       */
      const generateZIP = async (files) => {
        const zip = new JSZip();
        files.forEach((item) => {
          zip.file(item.name, item.blob, { binary: true });
        });
        const content = await zip.generateAsync({ type: "blob" });
        const currentDate = new Date().getTime();
        const fileName = `zipped-${currentDate}.zip`;
        return saveAs(content, fileName);
      };

demo下载

「JSZip Demo」