假设后端给了你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('请选择要下载的文件');
}
};