大文件上传、下载

329 阅读2分钟

大文件上传

  • 对文件做切片:将一个请求拆分成多个请求,每个请求的时间机会缩短,且如果某个请求失败,只需要重新发送这一次请求即可,无需重头开始;
  • 通知服务器合并切片:在上传完所有切片后,前端发送情趣通知服务器做切片处理;
  • 控制多个请求的并发量:防止多个请求同事发送,造成浏览器内存溢出,导致页面卡死;
  • 断点续传:当多个请求中有请求失败,例如出现网络故障,页面关闭等,我们要对失败的请求做处理,让它们重新发送;
  • 文件切片: 原理:文件FIle对象是Blob对象的子类,Blob对象包含一个重要的方法slice(),通过这个方法我们就可以对二进制文件进行拆分;
// 创建切片   
let size = 1024 * 50; //50KB 切片大小
let fileChunks = [];
let index = 0 //切片序号
for(let cur = 0; cur < file.size; cur += size){
  fileChunks.push({
      hash: index++,
      chunk: file.slice(cur, cur + size)
  })
}
  • 并发控制:
    结合Promise.race和异步函数实现多个请求同时并发的数量
  • 断点续传:
    单个请求失败后,触发catch方法的时候,把当前请求用一个失败列表存放起来,在本轮所有请求完成后,重复对失败请求做处理
const uploadFileChunks = async function(list){
  if(list.length === 0){
      //所有任务完成,合并切片
      await axios({
          method: 'get',
          url: '/merge',
          params: {
              filename: file.name
          }
      });
      console.log('上传完成')
      return
  }
  let pool = []//并发池
  let max = 3 //最大并发量
  let finish = 0//完成的数量
  let failList = []//失败的列表
  for(let i=0;i<list.length;i++){
      let item = list[i]
      let formData = new FormData()
      formData.append('filename', file.name)
      formData.append('hash', item.hash)
      formData.append('chunk', item.chunk)
      // 上传切片
      let task = axios({
          method: 'post',
          url: '/upload',
          data: formData
      })
      task.then((data)=>{
          //请求结束后将该Promise任务从并发池中移除
          let index = pool.findIndex(t=> t===task)
          pool.splice(index)
      }).catch(()=>{
          failList.push(item)
      }).finally(()=>{
          finish++
          //所有请求都请求完成
          if(finish===list.length){
              uploadFileChunks(failList)
          }
      })
      pool.push(task)
      if(pool.length === max){
          //每当并发池跑完一个任务,就再塞入一个任务
          await Promise.race(pool)
      }
  }
}