likes
comments
collection
share

大文件上传--切片上传(前端:Vue,后端:Nestjs)---前端部分

作者站长头像
站长
· 阅读数 6

前言

写网盘的项目的时候,需要文件上传功能,我使用的是nestjs框架做的后端,vue做的前端的一个全栈项目。 记录一下前后端大文件上传的详细知识。

这一篇是前端部分。

代码

前端

写秒传功能 思路:

  1. 秒传:前端需要切片读取这个文件的内容,并生成md5,后端根据这个是否有这个md5来判断是否上传,还是进行秒传。
  2. 切片上传:获取文件的大小,通过设置一个你想要切片的大小,然后通过计算算出需要切片的个数。之后判断是否后端有这个文件,有这个文件就是秒传,没有这个文件就是进行分片上传,当传完最后一个切片就进行合并文件。

秒传

//做秒传的,不做妙传的话可以不写,前端使用md5,后端通过这个md5来进行查询是否有这个md5
const computedMd5 = (fileItem: SingleFileStatus): any => {
  let file = fileItem.file
  let blobSlice = file.slice || file.mozSlice || file.webkitSlice
  //进行切分,总的切片数目,应该向上取整
  const chunks = Math.ceil(file.size / chunkSize)
  let currentIndex = 0
  const spark = new SparkMD5.ArrayBuffer()
  //读取文件
  const fileReader = new FileReader()
  //进行加载下一部分file
  const loadNext = () => {
    //设置截取区间
    let start = currentIndex * chunkSize
    let end = start + chunkSize > file.size ? file.size : start + chunkSize
    //进行截取
    fileReader.readAsArrayBuffer(blobSlice.call(file, start, end))
  }
  loadNext()
  return new Promise((resolve, reject) => {
    let resultFile = getFileById(file.uid) as SingleFileStatus
    fileReader.onload = (e) => {
      spark.append(e.target!.result)
      //切片的增加
      currentIndex++
      if (currentIndex < chunks) {
        console.log(`第${currentIndex}个切片完成`)
        //计算进度
        fileItem.md5Progress = Math.ceil((currentIndex / chunks) * 100)
        //读取下一个
        loadNext()
      } else {
        //最后一个切片,结束md5
        let md5 = spark.end()
        spark.destroy()
        //进行设置文件状态
        resultFile.md5Progress = 1
        resultFile.md5 = md5
        resultFile.status = STATUS.uploading.value
        resolve(fileItem.uid)
      }
    }
    fileReader.onerror = (err) => {
      resultFile.md5Progress = -1
      resultFile.status = STATUS.fail.value
      reject(fileItem.uid)
    }
  }).catch((err) => {
    console.log(err)
    return null
  })
}

上传其中有着文件状态的判断

//上传切片
const uploadChunk = async (uid: string | number, chunkIndex: number, fileHash: string) => {
  chunkIndex = chunkIndex ? chunkIndex : 0
  //寻找文件
  let currentFile = getFileById(uid) as SingleFileStatus
  //进行切片
  const file = currentFile.file
  const fileSize = currentFile.totalFileSize
  const chunks = Math.ceil(fileSize / chunkSize)
  const judjeFileStatus = async () => {
    //判断文件状态
    const verifyStatusParam: VerifyStatusParam = {
      fileSize: file.size,
      user_id: UserStore.userInfo._id,
      fileHash: fileHash,
      totalCount: chunks,
      filename: file.name + '-' + chunkIndex,
      file_type: file.type,
      filePid: FileStore.filePid
    }
    const verifyStatus = await VerifyStatusApi(verifyStatusParam)
    const data = verifyStatus.data
    if (data.length === 0 || data === '') {
      //秒传
      currentFile.md5Progress = 100
      currentFile.uploadProgress = 100
      currentFile.status = STATUS.upload_seconds.value
      //刷新重新获取文件
      reload()
      return 0
    } else {
      //可能是断点续传或者是一个新的上传
      //上传中
      currentFile.status = STATUS.uploading.value
      return 1
    }
  }
  const res = await judjeFileStatus()
  if (res === 0) {
    return
  }
  //分片上传
  for (let i = chunkIndex; i < chunks; i++) {
    //当前文件如果停止传输就不去传输了
    if (currentFile.pause) {
      break
    }
    //进行切分循环上传
    const start = i * chunkSize
    const end = start + chunkSize > fileSize ? fileSize + 1 : start + chunkSize
    //需要上传的内容
    const fileChunk = file.slice(start, end)
    //上传内容
    const ruleForm = {
      chunkIndex: i,
      fileHash: fileHash,
      filename: file.name + '-' + i,
      file_type: file.type
    }
    const otherForm = {
      currentFile: currentFile,
      fileSize: fileSize,
      index: i,
      chunkSize: chunkSize
    }
    const formData = new FormData()
    formData.append('file', fileChunk)
    await UploadChunkApi(formData, ruleForm, otherForm)
    if (chunks === i - 1) {
      currentFile.status = STATUS.upload_finish.value
    }
  }
  //最后一次上传
  if (
    currentFile.status === STATUS.upload_seconds.value ||
    currentFile.status === STATUS.upload_finish.value
  ) {
    //设置进度为100
    currentFile.uploadProgress = 100
    //进行合并
    const MergeParam: MergeParam = {
      filename: file.name,
      fileHash: fileHash,
      fileSize: fileSize,
      user_id: useUserInfo().userInfo._id,
      file_type: file.type,
      filePid: FileStore.filePid as string | number
    }
    const res = await MergeApi(MergeParam)
    if (res.code === 0) {
      //刷新重新获取文件
      reload()
      console.log(res, '合并成功了')
    }
  }
}

这一部分是寻找fileList中的文件

//根据id获取文件
const getFileById = (id: string | number) => {
  return fileList.value.find((item) => {
    return item.file.uid === id
  })
}
转载自:https://juejin.cn/post/7357142305424703542
评论
请登录