react项目中如何将多个文件压缩成zip下载

假设后端给了你20个文件url,前端如果一次下载一个,下载20次,浏览器底部将非常壮观,这不是一个优秀的前端。优秀的前端很容易想到将这些文件压缩成一个zip,这样就只需要下载一次就够了,设计非常的人性。

核心

1. bluebird // Promise扩展库
2. file-saver // 客户端保存文件的解决方案
3. JsZip // js创建、读取和编辑 .zip 文件

如何实现

html:

<Button type="primary" onClick={() => batchDownload(selectedRows)}>
  批量下载
</Button>

ts:

// url -> blob
const fetchBlob = (res: { url: string; filename: string }) => {
  // 原生fetch请求文件,拿到blob
  return fetch(res.url).then((resp) => ({
    blob: resp.blob(),
    filename: res.filename,
  }));
};



// 最终导出zip
const exportZip = (res: Array<{ blob: Promise<Blob>; filename: string }>) => {
  // 创建zip实例
  const zip = JsZip();
  // 向zip中添加文件(二进制文件流)
  res.forEach((item) => {
    zip.file(item.filename, item.blob);
  });
  // 异步生成zip,成功后下载zip
  zip
    .generateAsync({ type: 'blob' })
    .then((zipFile) =>
      FileSaver.saveAs(zipFile, 'xxx.zip'),
    );
};




// 点击批量下载按钮,records为选中的需要下载文件信息的集合
const batchDownload = (
  records: api.GetV1ContractModelGetContractListResponse['records'],
) => {
  if (records && records.length) {
  
    // -------- 判断文件名是否重复 ----------
    const personNames: string[] = [];
    let current = 0;
    const findIndex = (name: string) => {
      if (personNames.includes(name)) {
        current += 1;
        findIndex(`${name}-${current}`);
      } else {
        current = 0;
        personNames.push(name);
      }
      const res = personNames[personNames.length - 1].split('-');
      return res.length > 1 ? res[res.length - 1] : -1;
    };
    // -------- 判断文件名是否重复 ----------
    
    // Promise.map: http://bluebirdjs.com/docs/api/promise.map.html
    Promise.map(
      records.map((item) => {
        const arr = item.ossUrl.split('.');
        const surfix = arr[arr.length - 1]; // 文件后缀名
        let filename = '';
        const index = findIndex(item.personName);
        if (index !== -1) {
          // 文件名重复,重复的文件名将不会被下载下来 so bad,所以要加个索引标记
          filename = `xxx(${index}).${surfix}`;
        } else {
          // 文件名没有重复
          filename = `xxx.${surfix}`;
        }
        // 这里return出去的对象将被Promise.map的第二个参数所接收
        return { url: item.ossUrl, filename };
      }),
      async (res: { url: string; filename: string }) => {
        // 所有return出去的对象将被组装成数组后被Promise.map成功的回调函数(.then)接收
        return await fetchBlob(res);
      },
      { concurrency: 5 },
    ).then((res: Array<{ blob: Promise<Blob>; filename: string }>) => {
      exportZip(res);
    });
  } else {
    message.warning('请选择要下载的文件');
  }
};

你可能感兴趣的:(业务,react.js,javascript)