前端切片上传,控制并发上传实践方案

84 阅读1分钟

需求

项目需要大文件上传,大概3G左右的文件

思路

给大文件切片,1M为1片,每次控制并发100个请求,请求完后,再发100次请求,直至完成

实际

3G文件,5分钟左右,基本满足要求

代码

// 此处是上传文件核心代码
const uploadFile = async () => {
  // 取出文件
  const file = fileList.value[0];
  console.log(file, "----file");
  if (!file) {
    // element的提示
    ElMessage.error("未选择文件");
    return;
  }
  if (!file.name.includes(".")) {
    ElMessage.error("文件类型错误");
    return;
  }

  if (file.name.length > 30) {
    //判断文件大小
    ElMessage.error("文件名大于30个字符");
    return;
  }
  // 开始上传,设置标记位
  fileButtonDisabled.value = true;
  let chunkSize = 1024 * 1024 * 1; //分片大小1M
  // 切片数量
  let totalChunks = Math.ceil(file.size / chunkSize);
  console.log("totalChunks------", totalChunks);
  // 切片后,整个文件就靠这个编号识别了,就是文件的身份证
  let identifier = Date.now().toString();
  let ps = [];
  for (let i = 0; i < totalChunks; i++) {
    let start = i * chunkSize;
    let end = Math.min(start + chunkSize, file.size);
    let chunk = file.raw.slice(start, end);
    let formData = new FormData();
    formData.append("file", chunk);
    formData.append("fileName", file.name);
    formData.append("identifier", identifier);
    formData.append("chunkIndex", i);
    formData.append("totalChunks", totalChunks);
    ps.push(formData);
  }
  let num = 0;
  let limit = 100;
  while (ps.length > 0) {
    const listP = ps.splice(0, limit);
    // 并发请求
    await Promise.all(
      listP.map(item => {
        let f = api_upload_file(item);
        f.then(() => {
          sessionStorage.setItem("filePercentage", ++num);
          // 此处可是获取到实际上传进度值
          filePercentage.value = Math.floor(
            (+sessionStorage["filePercentage"] / totalChunks) * 100
          );
        });
        return f;
      })
    ).catch(err => {
      console.error(err);
      ElMessage.error("文件分片上传失败");
    });
  }
  // 请求后端合并
  merge(identifier);
};
const merge = async identifier => {
  let mergeFormData = new FormData();
  mergeFormData.append("identifier", identifier);
  // 请求完成之后,开始进行文件合并
  await api_merge_file(mergeFormData)
    .then(response => {
      if (response.data.success == true) {
        filePercentage.value = 100;
        ElMessage.success("文件上传成功");
        // 最终获取上传地址
        dialogForm.dataFilePath = response.data.data.url;
        dialogForm.dataFileName = response.data.data.name;
      } else {
        ElMessage.error(response.data.message);
      }
    })
    .catch(err => {
      ElMessage.error("文件上传失败");
      console.error(err);
    });
};