视频的分片上传,断点上传

发布于:2025-06-07 ⋅ 阅读:(17) ⋅ 点赞:(0)


上传功能的实现,点击上传按钮,判断添加的文件是否符合要求,如果符合把他放入文件列表中,并把他的状态设置为等待中,对于每个文件,把他们切分为chunksize大小的文件片段,再检查他的状态是否为uploading,并且判断他是否是第一次上传,如果是第一次上传,就从第一给分片上传,如果不是,就从之前上传的分片继续上传,同时,每上传完一个分片,uploadSize和uploadPercent就会更新,如果点击删除按钮,或者暂停上传,则中止上传

  1. 为每个视频文件创建新的属性,包括 uid、文件名、状态等信息,用于管理每个视频的上传任务。
  2. 创建一个视频数组 fileList,用来存放所有待上传的视频文件,并记录每个视频文件的状态、进度等。
  3. 实现文件分片上传。
const fileList = ref([]);
const addFile = (file) => {
  file = file.file
  if (fileList.value.length > sysSettingStore.sysSetting.videoPCount) {
    proxy.Message.warning(
      `最多可以添加${sysSettingStore.sysSetting.videoPCount}个视频`
    );
    return
  }
  let fileName = file.name
  const lastPoint = fileName.lastIndexOf('.')
  fileName = lastPoint == -1 ? fileName : fileName.substring(0, lastPoint)
  const fileItem = {
    file: file,
    uid: file.uid,
    fileName: fileName,
    status: STATUS.waiting.value,
    uploadSize: 0,
    totalSize: file.size,
    uploadPercent: 0,
    pause: false,
    chunkIndex: 0,
    errMsg: null,
  };
  fileList.value.push(fileItem);
  if (fileItem.totalSize == 0) {
    fileItem.status = STATUS.emptyfile.value;
    return;
  }
//   判断文件大小
  if (fileItem.totalSize > sysSettingStore.sysSetting.videoSize * 1024 * 1024) {
    fileItem.status = STATUS.largefile.value;
    return;
  }
  //   正在上传的文件
  let uploadingFiles = fileList.value.filter((item) => {
    return item.status == STATUS.uploading.value;
  });
  if (uploadingFiles.length > MAX_UPLOADING) {
    return;
  }
  uploadFile(fileItem.uid);
};

切片上传具体为:

1.根据getFileByUid()找到当前文件

// 通过uid找到当前文件
const getFileByUid = (uid) => {
  const currentFile = fileList.value.find((item) => {
    return item.uid == uid;
  });
  return currentFile;
};

2.对当前文件的状态设置为uploading,进行切片

3.请求生成一个上传 ID,uploadId 是服务器生成的唯一标识符,用于标识一个文件的上传任务。

当上传被暂停或中断时,使用相同的 uploadId 可以确保在恢复上传时,服务器能够继续处理文件的分片,而不是将其视为一个全新的文件上传任务。这样就避免了不同上传任务之间的混乱。
即使文件被分割成多个分片上传,所有分片都会与相同的 uploadId 关联。
同一个文件的所有分片的 uploadId 都是一致的。

4.进行断点处理

为什么设置chunkIndex = chunkIndex ? chunkIndex : 0?
这是为了支持断点续传, 即从上传进度的某个位置继续上传文件的剩余部分,而不是重新从头开始上传。

5.将文件的状态设置为success,进度为100%

6.开始上传下一个待上传的文件。

const uploadFile = async (uid, chunkIndex) => {
  // 当前文件
  const currentFile = getFileByUid(uid);
  // 文件状态设置为uploading
  currentFile.status = STATUS.uploading.value;

  const file = currentFile.file;
  const fileSize = currentFile.totalSize;
  const chunks = Math.ceil(fileSize / CHUNK_SIZE);
  if (!currentFile.uploadId) {
    // 如果第一次上传的时候,请求服务端生成一个uploadId
    let resultData = await proxy.Request({
      url: proxy.Api.preUploadVideo,
      params: {
        fileName: currentFile.fileName,
        chunks,
      },
      errorCallback: (errorMsg) => {
        currentFile.status = STATUS.fail.value;
        currentFile.errMsg = errorMsg;
      },
    });
    if (!resultData) {
      return;
    }
    currentFile.uploadId = resultData.data;
  }
//   for(let i=chunkIndex; i<chunks; i++) 中的
//   i=chunkIndex 表示从指定的分片索引 chunkIndex 开始上传,
//   而不是从 0 开始。这是为了支持断点续传, 即从上传进度的
//   某个位置继续上传文件的剩余部分,而不是重新从头开始上传。
//   循环处理
  chunkIndex = chunkIndex ? chunkIndex : 0;
  for (let i = chunkIndex; i < chunks; i++) {
    if (currentFile.pause || currentFile.del) {
      break;
    }
    let start = i * CHUNK_SIZE;
    let end = start + CHUNK_SIZE >= fileSize ? fileSize : start + CHUNK_SIZE;
    let chunkFile = file.slice(start, end);
    let uploadResult = await proxy.Request({
      url: proxy.Api.uploadVideo,
      dataType: "file",
      params: {
        chunkFile: chunkFile,
        chunkIndex: i,
        uploadId: currentFile.uploadId,
      },
      showError: false,
      errorCallback: (errorMsg) => {
        currentFile.status = STATUS.fail.value;
        currentFile.errMsg = errorMsg;
      },
      uploadProgressCallback: (event) => {
        // event.loaded 表示已上传的字节数。
        let loaded = event.loaded;
        if (loaded > fileSize) {
          loaded = fileSize;
        }
        currentFile.uploadSize = i * CHUNK_SIZE + loaded;
        currentFile.uploadPercent = Math.floor(
          (currentFile.uploadSize / fileSize) * 100
        );
      },
    });
    if (uploadResult == null) {
      break;
    }
    currentFile.chunkIndex = i;
    if (i < chunks - 1) {
      continue;
    }
    currentFile.status = STATUS.success.value;
    currentFile.uploadPercent = 100;
  }
  const nextItem = fileList.value.find((item) => {
    return item.status == STATUS.waiting.value;
  });
  if (nextItem) {
    uploadFile(nextItem.uid);
  }
};

最后转化为字符串提交

uploadFileList:JSON.stringify(uploadFileList)