react下载1500个文件,实现分批次6个下载

523 阅读2分钟

下载.jpeg

前言

公司的素材管理模块,客户下载文件1500个,在生产环境中,下载报错# ERR_INSUFFICIENT_RESOURCES,查找原因,是使用for循环创建了1500个获取资源的请求,超出了浏览器发起请求最大资源(1350个左右,自己试验总结),于是产生了队列上传的想法,同时最多有6个上传。

代码搬运

const dataRequest = [
    {
        "file_name": "/免洗洗手液 松木50ml-69029424/主图/69029424-3.jpg",
        "file_url": "https://mcms-manon.oss-cn-shanghai.aliyuncs.com/prod/700/image/materials/202204/41/rc-upload-1650774625197-552.jpg"
    },
    {
        "file_name": "/免洗洗手液 松木50ml-69029424/主图/69029424-2.jpg",
        "file_url": "https://mcms-manon.oss-cn-shanghai.aliyuncs.com/prod/700/image/materials/202204/41/rc-upload-1650774625197-551.jpg"
    },
    {
        "file_name": "/免洗洗手液 松木50ml-69029424/主图/69029424-1.jpg",
        "file_url": "https://mcms-manon.oss-cn-shanghai.aliyuncs.com/prod/700/image/materials/202204/41/rc-upload-1650774625197-550.jpg"
    },
    {
        "file_name": "/免洗洗手液 松木50ml-69029424/详情页/69029424-23.jpg",
        "file_url": "https://mcms-manon.oss-cn-shanghai.aliyuncs.com/prod/700/image/materials/202204/41/rc-upload-1650774625197-573.jpg"
    },
]

let _that = this
this.sendRequest(dataRequest,6,_that,zip,()=>{
    zip.generateAsync({ type: 'blob',compression: "DEFLATE", 
        compressionOptions: {
        level: 9} // 压缩等级1~9 1压缩速度最快,9最优压缩方式
    }).then((content) => {
      this.setState({downloadspinning:false,successNum:0,totalFileNum:0,})
      // 生成二进制流,利用file-saver保存文件
      saveAs(content, `${批量下载}.zip`);
    })
})

//请求文件资源
getFile_v2 =(url,index,item)=>{
    return new Promise((resolve, reject) => {
      axios({
        method: 'get',
        url:url + `?` +Date.now(),
        responseType: 'blob',
        }).then((data) => {
          resolve(data.data);
          this.setState({successNum:this.state.successNum+1})
        }).catch((error) => {
          reject(error.toString());
        });
      });
}

sendRequest = (urls, max, _that, zip, callbackFunc)=> {
    // console.log(urls);
    const REQUEST_MAX = max;
    const TOTAL_REQUESTS_NUM = urls.length;
    const blockQueue = []; // 等待排队的那个队列
    let currentReqNumber = 0; // 现在请求的数量是
    let numberOfRequestsDone = 0; // 已经请求完毕的数量是
    const results = new Array(TOTAL_REQUESTS_NUM).fill(false); // 所有请求的返回结果,先初始化上

    async function init() {
      // console.log('========init');
      for (let i = 0; i < urls.length; i++) {
        request(i, urls[i], _that);
      }
    }

    async function request(index, reqUrl,_that) {
      // console.log('========request');
      // 这个index传过来就是为了对应好哪个请求,
      // 放在对应的results数组对应位置上的,保持顺序
      if (currentReqNumber >= REQUEST_MAX) {
        await new Promise((resolve) => blockQueue.push(resolve)); // 阻塞队列增加一个 Pending 状态的 Promise,
        // 进里面排队去吧,不放你出来,不resolve你,你就别想进行下面的请求
      }
      reqHandler(index, reqUrl, _that); // {4}
    }

    async function reqHandler(index, reqUrl,_that) {
      // console.log('=====reqHandler',_that);
      currentReqNumber++; // {5}
      try {
        const res = await _that.getFile_v2(reqUrl.file_url);
        results[index] = res;
        zip.file(reqUrl.file_name, res, { binary: true }); 
      } catch (err) {
        results[index] = err;
      } finally {
        currentReqNumber--;
        numberOfRequestsDone++;
        if (blockQueue.length) {
          // 每完成一个就从阻塞队列里剔除一个
          blockQueue[0](); // 将最先进入阻塞队列的 Promise 从 Pending 变为 Fulfilled,
          // 也就是执行resolve函数了,后面不就能继续进行了嘛
          blockQueue.shift();
        }
        if (numberOfRequestsDone === TOTAL_REQUESTS_NUM) {
          callbackFunc(results);
        }
      }
    }

    init();
}

参考

前端面试题:实现批量请求数据,并控制请求并发数量,最后所有请求结束之后,执行callback回调函数_擦拉嘿的博客-CSDN博客_实现一个批量请求函数, 能够限制并发量?