网盘项目总结:js大文件分片上传

113 阅读2分钟

通过input框拿到file对象后

// 通过SparkMD5插件来计算文件的md5
import SparkMD5 from 'spark-md5'
//计算文件的md5值
function computeMd5(file, uploadFile) {
  return new Promise((resolve, reject) => {
    //大文件分片读取并计算md5,提高速度

    const chunkTotal = 100 //分片数
    const chunkSize = Math.ceil(file.size / chunkTotal)
    const fileReader = new FileReader()
    const md5 = new SparkMD5()
    let index = 0
    const loadFile = uploadFile => {
      uploadFile.parsePercentage.value = parseInt((index / file.size) * 100)
      const slice = file.slice(index, index + chunkSize)

      fileReader.readAsBinaryString(slice)
    }
    loadFile(uploadFile)
    fileReader.onload = e => {
      md5.appendBinary(e.target.result)
      if (index < file.size) {
        index += chunkSize
        loadFile(uploadFile)
      } else {
        // md5.end() 就是文件md5码
        resolve(md5.end())
      }
    }
  })
}
// 文件上传前的准备工作
beforeUpload(){
    let uploadFile = {}
    uploadFile.name = file.name
    uploadFile.size = file.size
    uploadFile.parsePercentage = ref(0)
    uploadFile.uploadPercentage = ref(0)
    uploadFile.uploadSpeed = '0 M/s'
    uploadFile.chunkList = null
    uploadFile.file = file
    uploadFile.uploadingStop = false
    uploadFile.index = uploadFileList.value.length
    uploadFileList.value.push(uploadFile)
    let md5 = await computeMd5(file, uploadFile) //计算出文件的md5
    uploadFile.md5 = md5
    let res = await checkFileByMd5({ md5 }) //上传服务器检查,以确认是否秒传
    if (Array.isArray(res)) { // 不秒传
      uploadFile.chunkList = res
      uploadFile.needUpload = true
    } else { // 秒传
      uploadFile.needUpload = false
      uploadFile.uploadPercentage.value = 100
      ElMessage.success('文件已秒传!')
      dialogVisible.value = false
    }
    uploadChunk(file, 1, uploadFile)
}

function uploadChunk(file, index, uploadFile) {
 let chunkSize = 1024 * 1024 * 24 //24mb
 let chunkTotal = Math.ceil(file.size / chunkSize)
 if (index <= chunkTotal) {
    // 根据是否暂停,确定是否继续上传
    let startTime = new Date().valueOf()

    let exit = uploadFile.chunkList.includes(index)
    // console.log("是否存在",exit);

    if (!exit) {
      if (!uploadFile.uploadingStop) {
        // 分片上传,同时计算进度条和上传速度
        // 已经上传的不在上传、
        // 上传完成后提示,上传成功
        let formdata = new FormData()
        let start = (index - 1) * chunkSize
        let end = index * chunkSize >= file.size ? file.size : index * chunkSize
        let chunk = file.slice(start, end)

        formdata.append('chunk', chunk)

        let type = filterFileClassify(file.name)

        let chunkForm = {
          chunkSize: chunkSize,
          chunkTotal: chunkTotal,
          fileName: file.name,
          fileSize: file.size,
          index: index,
          md5: uploadFile.md5,
          type: type
        }
        uploadChunkFile(chunkForm, formdata).then(res => {
          let endTime = new Date().valueOf()
          let timeDif = (endTime - startTime) / 1000
          //上传速度
          uploadFile.uploadSpeed = (10 / timeDif).toFixed(1) + ' M/s'
          uploadFile.chunkList.push(index)
          //上传进度
          uploadFile.uploadPercentage = parseInt((uploadFile.chunkList.length / chunkTotal) * 100)
          if (index == chunkTotal) {
          // 调后台接口
            bindBigFile(currentId.value, res).then(() => {
              ElMessage.success('文件上传成功!')
              //uploadRef是 elementPlu 的 el-upload的节点对象
              uploadRef.value.clearFiles()
            })
          }
          uploadChunk(file, index + 1, uploadFile)
        })
      }
    } else {
      uploadFile.uploadPercentage = parseInt((uploadFile.chunkList.length / chunkTotal) * 100)

      uploadChunk(file, index + 1, uploadFile)
    }
  }
}
//点击下载文件分片
function downloadChunk(index, file) {
  isDowning.value = true
  let chunkSize = 1024 * 1024 * 5
  let chunkTotal = Math.ceil(file.size / chunkSize)

  if (index <= chunkTotal) {
    // console.log("下载进度",index);
    let exit = file.chunkList.includes(index)
    console.log('存在', exit)

    if (!exit) {
      if (!file.downloadingStop) {
        let startTime = new Date().valueOf()

        downloadBigFile({
          chunkSize: index * chunkSize >= file.size ? file.size - (index - 1) * chunkSize : chunkSize,
          chunkTotal: chunkTotal,
          fileName: file.name,
          index: index,
          md5: file.md5
        }).then(res => {
          file.chunkList.push(index)
          let endTime = new Date().valueOf()
          let timeDif = (endTime - startTime) / 1000
          file.downloadSpeed = (5 / timeDif).toFixed(1) + ' M/s'

          file.downloadPersentage = parseInt((index / chunkTotal) * 100)

          const blob = res

          file.blobList.push(blob)
          //切片下载完成
          if (index == chunkTotal) {
            let resBlob = new Blob(file.blobList, {
              type: 'application/octet-stream'
            })

            let url = window.URL.createObjectURL(resBlob) // 将获取的文件转化为blob格式
            let a = document.createElement('a') // 此处向下是打开一个储存位置
            a.style.display = 'none'
            a.href = url
            // 下面两行是自己项目需要的处理,总之就是得到下载的文件名(加后缀)即可

            let fileName = file.name

            a.setAttribute('download', fileName)
            document.body.appendChild(a)
            a.click() //点击下载

            document.body.removeChild(a) // 下载完成移除元素
            window.URL.revokeObjectURL(url) // 释放掉blob对象

            //预览1.5s 浏览器处理文件
            let timer = setTimeout(() => {
              file.downloadPersentage = 100
              isDowning.value = false
              console.log('isDowning', isDowning.value)
              //下载成功后,列表中清除当前的大文件
              downloadingFileList.value.splice(
                downloadingFileList.value.findIndex(ele => ele.id === file.id),
                1
              )
              console.log(downloadingFileList)
              if (downloadingFileList.value.length === 0) {
                handleClose()
              }
              clearTimeout(timer)
              timer = null
              file = null
            }, 1500)
          }

          downloadChunk(index + 1, file)
        })
      }
    } else {
      file.downloadPersentage = parseInt((index / chunkTotal) * 100)
      downloadChunk(index + 1, file)
    }
  }
}