如何绕开浏览器批量下载的限制

前言

最近遇到一个需求,需要将批量选择的图片,批量一个个下载。

触发单个下载

在浏览器中触发下载,我们可以借用a元素来触发。

ts 复制代码
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来批量执行就好了

ts 复制代码
export const batchDownloadFile = async (files: { url: string; name: string; }[], showLoading = true) => {
  if (showLoading) {
  // 全局loading,可以忽略
    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来分批下载即可。

ts 复制代码
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个文件。

相关推荐
无限大.33 分钟前
前端知识速记:节流与防抖
前端
十八朵郁金香35 分钟前
【VUE案例练习】前端vue2+element-ui,后端nodo+express实现‘‘文件上传/删除‘‘功能
前端·javascript·vue.js
学问小小谢39 分钟前
第26节课:内容安全策略(CSP)—构建安全网页的防御盾
运维·服务器·前端·网络·学习·安全
LCG元1 小时前
Vue.js组件开发-实现全屏图片文字缩放切换特效
前端·javascript·vue.js
还是鼠鼠2 小时前
图书管理系统 Axios 源码__新增图书
前端·javascript·vscode·ajax·前端框架·node.js·bootstrap
还是鼠鼠5 小时前
图书管理系统 Axios 源码 __删除图书功能
前端·javascript·vscode·ajax·前端框架·node.js·bootstrap
轻口味5 小时前
Vue.js `Suspense` 和异步组件加载
前端·javascript·vue.js
m0_zj7 小时前
8.[前端开发-CSS]Day08-图形-字体-字体图标-元素定位
前端·css
还是鼠鼠7 小时前
图书管理系统 Axios 源码__编辑图书
前端·javascript·vscode·ajax·前端框架
北极象7 小时前
vue3中el-input无法获得焦点的问题
前端·javascript·vue.js