在实际开发中,我们经常会遇到需要对后端返回的多个 ZIP 文件进行处理,然后打包成一个主 ZIP 文件供用户下载的需求。本文将详细解析下面这段示例代码,讲解其工作原理以及各个步骤的实现方法,希望能为你在实际项目中的类似需求提供一些参考。
实现思路
本示例主要完成以下任务:
- 加载动画提示:用户在等待处理过程中,通过加载动画给出友好的提示。
- 获取文件列表:从路由参数中获取省份名称,并调用 API 接口获取对应的文件列表。
- 并行下载与处理:使用
Promise.all
并行下载每个 ZIP 文件的二进制内容,并利用 JSZip 读取其内部内容。 - 文件重命名及打包:遍历每个 ZIP 文件中的子文件,对文件名添加唯一 ID 后重命名,再将处理后的文件添加到新的 ZIP 包中。
- 生成主 ZIP 文件并触发下载:最终生成一个包含所有处理后 ZIP 文件的主 ZIP,并通过创建下载链接实现自动下载。
- 错误处理:在各个步骤中捕获并处理可能出现的错误,确保用户体验。
代码解析
下面逐步解析代码的主要部分:
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();
});
},
希望这篇文章能帮助大家在实际项目中快速实现类似的功能。如果你有任何问题或改进建议,欢迎在评论区留言交流!
这就是本次分享的全部内容,祝大家在项目中编程愉快!