优化实战 第 63 期 - 下载大于2G的多文件到ZIP压缩包

2,025 阅读1分钟

客户反馈无法同时下载 30 个短视频文件的 Zip 压缩包到本地,经排查发现下载文件的总大小超过了 2G,不仅在文件的下载过程中造成了浏览器的卡顿,还在压缩文件时出现缓存区错误

error.png

对下载文件的异步任务限流

  • 实现原理

    可参阅 第 03 期 - 对异步任务并发量进行限流 一文中的优化思路

  • 方法封装

    const invariable = async ({ data:list = [], limit }, callback) => {
      window.requestIdleCallback(function(deadline) {
        const queue = list.splice(0, limit)
        while(queue.length) {
          const { url, filename } = queue.shift()
          fetch(url)
            .then(response => {
              const { ok, status, statusText, body: stream } = response
              if (!ok) {
                return Promise.reject({ status, statusText })
              } else {
                return stream.getReader()
              }
            })
            .then(async reader => {
              const chunks = []
              while(true) {
                const { done, value } = await reader.read()
                if (!done) {
                  chunks.push(value)
                } else {
                  callback({ filename, data: new Blob(chunks) })
                  if (list.length) invariable({ data: list, limit: 1 }, callback)
                  break
                }
              }
            })
            .catch(error => console.log(error))
        }
      }, { timeout: 5000 })
    }
    

通过分包实现ZIP文件下载

  • 实现原理

    设置一定的分组数量,编写分组算法实现多包下载,并给包命名添加序号说明

    zip.png

  • 方法封装

    import JSZip from 'jszip'
    const download = ({data, zipName, groupNum = 10}, callback) => {
      const total = data.length, totalZipPkg = Math.ceil(total / groupNum)
      let zip = new JSZip(), currentDownloads = 0, serialNumber = 0
      invariable({
        data,
        limit: 3,
      }, ({ filename, data }) => {
          zip.file(filename, data)
          currentDownloads += 1
          if ((currentDownloads % groupNum === 0) || (currentDownloads % groupNum > 0 && currentDownloads === total)) {
            zip.generateAsync({
              // 压缩等级 1~9,1 压缩速度最快,9 最优压缩方式
              type: 'blob', compressionOptions: { level: 9 } 
            }).then(blob => {
              serialNumber += 1, zipName = zipName || `${new Date().toLocaleString()}.zip`
              saveAs(blob, `[${serialNumber}-${totalZipPkg}]-${zipName}`)
            })
            zip = new JSZip()
         }
         callback(currentDownloads === total ? { done: true } : { type: 'download', done: false, value: currentDownloads })
      })
    }
    

    方法中所引用的 saveAs 方法可参阅 第 45 期 - 基于二进制流实现文件下载 一文中的优化思路

    一起学习,加群交流看 沸点