通过a标签批量下载,而浏览器限制一次批量下载最多10个,剩余的会自动忽略,突破限制来了

256 阅读2分钟

背景

之前系统的功能只是单独展示二维码和下载单个二维码图片。解析出二维码的内容是由前端决定的,再使用qrcode生成二维码在网页展示。最近业务需要批量下载二维码图片,考虑到由于二维码的图片大小比较小和批量下载的条数不会太多,因此可以在前端实现批量下载二维码图片功能的方案。

解决方法

一开始我直接取到用户勾选到的全部数据,然后利用for循环来通过a标签下载一张二维码图片,结果发现浏览器只能下载10张,超过10张后面的就不会下载了。这个是浏览器的保护机制,我只能把这些数据分批处理,分批的时候可以用到promise。同时封装了useBatch通用函数工具。

具体简化代码示例

示例代码可以放在html文件的srcipt标签中运行

    // 假设这是你需要批量下载的内容
    const list = Array.from({ length: 20 }, (v, i) => ({
      fileName: `${i}`, // 文件名
      txt: `${i}`,     // 二维码内容
    }));

    // 这里是对list里每一项内容自定义处理的函数
    async function handleExport(item, index) {
      // 调用下载函数,传入文件名和二维码内容
      download(item.fileName, item.txt);
    }

    // a标签下载函数
    function download(fileName = '二维码图片文件名', txt = '文字内容') {
      const newWidth = 300;  // 二维码宽度
      const newHeight = 300; // 二维码高度
      const newCanvas = document.createElement('canvas'); // 创建一个新的canvas元素
      newCanvas.width = newWidth;
      newCanvas.height = newHeight;
      const ctx = newCanvas.getContext('2d'); // 获取2D绘图上下文

      // 设置背景为白色
      ctx.fillStyle = '#ffffff';
      ctx.fillRect(0, 0, newWidth, newHeight);

      // 设置字体样式
      ctx.font = '18px Arial';
      ctx.fillStyle = '#45a8e2'; // 设置文字颜色
      ctx.textAlign = 'center';   // 设置文字居中
      ctx.fillText(txt, newWidth / 2, newHeight / 2); // 绘制文字

      // 将canvas内容转换为Data URL
      const url = newCanvas.toDataURL();
      const a = document.createElement('a'); // 创建一个a标签
      a.download = `${fileName}.png`; // 设置下载文件名
      a.href = url; // 设置a标签的href属性
      document.body.appendChild(a); // 将a标签添加到body中
      a.click(); // 触发点击事件以开始下载
      document.body.removeChild(a); // 下载完成后移除a标签
    }

    // 批量下载
    function startBatchDown() {
      useBatch({
        list,    // 需要批量处理的数据列表
        fn: handleExport, // 处理每一项数据的函数
        count: 5, // 每批处理的数量
      });
    }

    // 通用的 批量下载工具函数
    async function useBatch({ list = [], fn = () => {}, count = 5, time = 1000 }) {
      const batches = [];

      // 将列表分批
      for (let i = 0; i < list.length; i += count) {
        batches.push(list.slice(i, i + count));
      }

      // 处理每一批数据
      for (const batch of batches) {
        // 将每项数据映射为一个Promise
        const batchPromises = batch.map((item, index) => fn(item, index));

        // 并行处理当前批次的所有Promise
        await Promise.all(batchPromises);

        // 控制每批处理的时间间隔
        await new Promise((resolve) => setTimeout(resolve, time));
      }
    }

    // 调用批量下载函数
    startBatchDown();