前言
最近遇到一个需求,需要将批量选择的图片,批量一个个下载。
触发单个下载
在浏览器中触发下载,我们可以借用 a 元素来触发。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const downloadFile = async (url: string, name: string) => { const res = await fetch(url); const blob = await res.blob();
const strList = url.split('.'); const type = strList[strList.length - 1];
const downUrl = window.URL.createObjectURL(blob); const downloadElement = document.createElement('a'); document.body.appendChild(downloadElement); downloadElement.href = downUrl; downloadElement.download = `${name}.${type}`; downloadElement.click(); document.body.removeChild(downloadElement); window.URL.revokeObjectURL(downUrl); };
|
批量触发
单个实现了,批量就 promise 来批量执行就好了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| export const batchDownloadFile = async (files: { url: string; name: string; }[], showLoading = true) => { if (showLoading) { loading.value = ElLoading.service({ lock: true, text: '正在下载中...', background: 'rgba(0, 0, 0, 0.7)', }); }
const promiseList = files.map((file) => { return new Promise(async (resolve, reject) => { try { downloadFile(file.url, file.name) resolve(null); } catch { reject('下载错误'); } }); });
const result = await Promise.allSettled(promiseList);
if (showLoading) { loading.value?.close(); }
return result.map((item, index) => { return { isSuccess: item.status === 'fulfilled', name: files[index].name, url: files[index].url, }; }); };
|
使用 allSettled 而不用 all 是因为,Promise.all 是有一个被拒绝就会被拒绝,不符合部分成功的情况,所以这里使用的是 allSettled。
浏览器限制
对于一次性(即一次宏任务的 event loop)触发下载,浏览器会限制一次触发 a 元素下载最多 10 次。
对于下载选择的图片超过 10 张的情况下,要使用分批来下载。
使用 setTimeout 来绕过限制
既然一次宏任务限制 10 个,那么就借用 setTimeout 来分批下载即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| export const batchDownloadFile = async (files: { url: string; name: string; }[], showLoading = true) => { const batchSize = 10; const delay = 1000;
if (showLoading) { loading.value = ElLoading.service({ lock: true, text: '正在下载中...', background: 'rgba(0, 0, 0, 0.7)', }); }
const result: { isSuccess: boolean; name: string; url: string; }[] = [];
const downloadBatch = async (batch: { url: string; name: string; }[]) => { const promiseList = batch.map((file) => { return new Promise(async (resolve, reject) => { try { await downloadFile(file.url, file.name); resolve(null); } catch { reject('下载错误'); } }); });
const batchResult = await Promise.allSettled(promiseList); batchResult.forEach((item, index) => { result.push({ isSuccess: item.status === 'fulfilled', name: batch[index].name, url: batch[index].url, }); }); };
const delayPromise = (ms: number) => new Promise((resolve) => { setTimeout(resolve, ms); });
const processBatches = async (remainingFiles: { url: string; name: string; }[]) => { if (remainingFiles.length === 0) { return; }
const batch = remainingFiles.slice(0, batchSize); const remaining = remainingFiles.slice(batchSize);
await downloadBatch(batch); await delayPromise(delay); await processBatches(remaining); };
await processBatches(files);
if (showLoading) { loading.value?.close(); }
return result; };
|
改动后,顺利实现了批量下载超过 10 个文件。
