使用 JSZip 实现批量解压ZIP文件,修改文件名称并下载

在实际开发中,我们经常会遇到需要对后端返回的多个 ZIP 文件进行处理,然后打包成一个主 ZIP 文件供用户下载的需求。本文将详细解析下面这段示例代码,讲解其工作原理以及各个步骤的实现方法,希望能为你在实际项目中的类似需求提供一些参考。

实现思路

本示例主要完成以下任务:

  1. 加载动画提示:用户在等待处理过程中,通过加载动画给出友好的提示。
  2. 获取文件列表:从路由参数中获取省份名称,并调用 API 接口获取对应的文件列表。
  3. 并行下载与处理:使用 Promise.all 并行下载每个 ZIP 文件的二进制内容,并利用 JSZip 读取其内部内容。
  4. 文件重命名及打包:遍历每个 ZIP 文件中的子文件,对文件名添加唯一 ID 后重命名,再将处理后的文件添加到新的 ZIP 包中。
  5. 生成主 ZIP 文件并触发下载:最终生成一个包含所有处理后 ZIP 文件的主 ZIP,并通过创建下载链接实现自动下载。
  6. 错误处理:在各个步骤中捕获并处理可能出现的错误,确保用户体验。

代码解析

下面逐步解析代码的主要部分:

1. 创建加载动画

在文件处理开始之前,通过 this.$loading 创建加载动画,提示用户“正在处理文件,请稍候…”。这样可以在长时间操作过程中提升用户体验。

const loading = this.$loading({
  lock: true,
  text: '正在处理文件,请稍候...',
  spinner: 'el-icon-loading',
  background: 'rgba(0, 0, 0, 0.7)'
});

2. 获取省份名称和文件列表

通过 this.$route.query.name 获取路由参数中的省份名称,并将其作为参数调用 API 接口 every_zhishu_file,获取该省份下的文件列表。

let provinceName = this.$route.query.name;
every_zhishu_file({ provinceName: provinceName }).then(res => {
  const testFiles = res.data;
  // 接下来的操作将在获取到文件列表后进行
});

3. 并行下载与读取 ZIP 文件

使用 Promise.all 方法并行下载所有文件,通过 fetch 获取文件的二进制 blob,再利用 JSZip 的 loadAsync 方法解析每个 ZIP 文件的内容。

Promise.all(
  testFiles.map(file =>
    fetch(file.path)
      .then(response => response.blob())
      .then(blob => ({ blob, file }))
  )
).then(async results => {
  // results 数组中包含每个文件的 blob 和文件信息
});

这种并行处理方式可以大大提升处理效率,避免串行加载时的等待时间。

4. 遍历并处理 ZIP 文件中的子文件

对每个下载后的 ZIP 文件,使用 JSZip 解析后遍历其中的所有文件。如果当前项不是文件夹,则获取其内容,并在原文件名中添加文件的唯一 ID(通过 file.id)来生成新文件名,最后将处理后的文件添加到新的 ZIP 包中。

for (const { blob, file } of results) {
  try {
    const individualZip = await JSZip.loadAsync(blob);
    const newZip = new JSZip();

    for (const [originalFileName, zipEntry] of Object.entries(individualZip.files)) {
      if (!zipEntry.dir) {
        const content = await zipEntry.async('blob');
        const fileNameParts = originalFileName.split('.');
        const newFileName = `${fileNameParts[0]}_${file.id}.${fileNameParts[1]}`;
        newZip.file(newFileName, content);
      }
    }

    // 生成处理后的 ZIP Blob
    const newZipBlob = await newZip.generateAsync({ type: 'blob' });
    
    // 从文件路径中提取中文名称,并添加文件 id 生成新 ZIP 文件名称
    const chineseStart = file.path.match(/[\u4e00-\u9fa5]/).index;
    const extractedName = file.path.slice(chineseStart).replace('.zip', `${file.id}.zip`);
    
    // 将新生成的 ZIP 添加到主 ZIP 中
    mainZip.file(`${extractedName}`, newZipBlob);
  } catch (error) {
    console.error(`处理文件 ${file.deptName} 时出错:`, error);
  }
}

这种处理方式不仅能保持原文件结构,还能在文件名称上标识出不同的文件来源,便于后续查找和使用。

5. 生成主 ZIP 文件并自动下载

所有文件处理完毕后,利用 JSZip 将所有处理后的文件打包生成最终的 ZIP 文件,然后创建一个临时的下载链接并模拟点击,从而实现自动下载。

mainZip.generateAsync({ type: "blob" }).then(content => {
  const url = window.URL.createObjectURL(content);
  const a = document.createElement('a');
  a.href = url;
  a.download = `${provinceName}.zip`;
  document.body.appendChild(a);
  a.click();
  window.URL.revokeObjectURL(url);
  document.body.removeChild(a);
});

6. 错误处理与清理工作

在整个异步流程中,代码采用了 .catch 捕获错误,并使用 this.$message.error 提示用户文件处理失败。同时,使用 .finally 关闭加载动画,保证无论成功或失败都能及时释放加载状态。

}).catch(error => {
  this.$message.error('文件处理失败:' + error.message);
}).finally(() => {
  loading.close();
});

总结

本文介绍的示例代码展示了如何利用 JSZip 库与现代 JavaScript 异步编程技巧(如 Promise、async/await)来实现批量 ZIP 文件的处理与下载。主要流程包括:

  • 加载动画提示:提升用户体验。
  • 并行下载与处理:利用 Promise.all 加快处理速度。
  • ZIP 文件内部处理:使用 JSZip 解压、修改文件名、重新打包。
  • 自动下载:通过创建下载链接实现最终文件下载。
  • 错误处理:完善的错误捕获与提示,确保操作的健壮性。

全部代码

jszip() {
      // 创建加载动画
      const loading = this.$loading({
        lock: true,
        text: '正在处理文件,请稍候...',
        spinner: 'el-icon-loading',
        background: 'rgba(0, 0, 0, 0.7)'
      });
      // 从路由参数中获取省份名称
      let provinceName = this.$route.query.name
      // 调用API获取直属文件列表
      every_zhishu_file({provinceName:provinceName}).then(res => {
        // 创建主zip文件对象
        const mainZip = new JSZip();

        // 获取返回的文件列表
        const testFiles = res.data

        // 并行处理所有文件
        Promise.all(
          testFiles.map(file =>
            // 获取每个文件的二进制内容
            fetch(file.path)
              .then(response => response.blob())
              .then(blob => ({blob, file}))
          )
        ).then(async results => {
          console.log(results);
          // 遍历处理每个文件
          for (const {blob, file} of results) {
            try {
              // 读取每个zip文件内容
              const individualZip = await JSZip.loadAsync(blob);
              // 创建新的zip对象用于存储处理后的文件
              const newZip = new JSZip();

              // 遍历zip中的每个文件
              for (const [originalFileName, zipEntry] of Object.entries(individualZip.files)) {
                if (!zipEntry.dir) {
                  // 获取文件内容
                  const content = await zipEntry.async('blob');

                  // 在原文件名后添加id作为新文件名
                  const fileNameParts = originalFileName.split('.');
                  const newFileName = `${fileNameParts[0]}_${file.id}.${fileNameParts[1]}`;

                  // 将处理后的文件添加到新zip中
                  newZip.file(newFileName, content);
                }
              }

              // 生成处理后的zip文件
              const newZipBlob = await newZip.generateAsync({type: 'blob'});
              // 提取中文名称并添加id
              const chineseStart = file.path.match(/[\u4e00-\u9fa5]/).index;
              const extractedName = file.path.slice(chineseStart).replace('.zip', `${file.id}.zip`);
              // 将处理后的zip添加到主zip中
              mainZip.file(`${extractedName}`, newZipBlob);
            } catch (error) {
              // 错误处理
              console.error(`处理文件 ${file.deptName} 时出错:`, error);
            }
          }

          // 生成最终的zip文件并触发下载
          mainZip.generateAsync({type: "blob"}).then(content => {
            // 创建下载链接
            const url = window.URL.createObjectURL(content);
            const a = document.createElement('a');
            a.href = url;
            a.download = `${provinceName}.zip`;
            // 添加到页面并触发点击
            document.body.appendChild(a);
            a.click();
            // 清理
            window.URL.revokeObjectURL(url);
            document.body.removeChild(a);
          });
        });
      }).catch(error => {
        // 错误提示
        this.$message.error('文件处理失败:' + error.message);
      }).finally(() => {
        // 关闭加载动画
        loading.close();
      });
    },

希望这篇文章能帮助大家在实际项目中快速实现类似的功能。如果你有任何问题或改进建议,欢迎在评论区留言交流!


这就是本次分享的全部内容,祝大家在项目中编程愉快!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端程序猿i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值