前端大文件分片上传

直接看代码

<template>
  <h1>大文件上传</h1>
  <input type="file" @change="handleUpload">
</template>

<script setup>
import SparkMD5 from 'spark-md5'

const CHUNK_SIZE = 1024 * 1024; //1MB
const fileHash = ref('')
const fileName = ref('')

/** 文件分片 **/
const createChunks = (file) => {
  let cur = 0;
  let chunks = []
  while (cur < file.size) {
    const blob = file.slice(cur, cur + CHUNK_SIZE)
    chunks.push(blob)
    cur += CHUNK_SIZE
  }
  return chunks
}

/** hash计算 **/
const calculateHash = (chunks) => {
  return new Promise(resolve => {
    const targets = [] //存储参与计算的切片

    const spark = new SparkMD5.ArrayBuffer()
    const fileReader = new FileReader()

    chunks.forEach((chunk, index) => {
      // 第一个和最后一个切片参与计算
      if (index === 0 || index === chunks.length - 1) {
        targets.push(chunk)
      } else {
        // 中间的切片只计算前面两个字节 中间两个字节  最后两个字节
        targets.push(chunk.slice(0, 2))
        targets.push(chunk.slice(CHUNK_SIZE / 2, CHUNK_SIZE / 2 + 2))
        targets.push(chunk.slice(CHUNK_SIZE - 2, CHUNK_SIZE))
      }
    })
    fileReader.readAsArrayBuffer(new Blob(targets))
    fileReader.onload = (e) => {
      spark.append(e.target.result)
      //计算的hash值
      // console.log('hash', spark.end())
      resolve(spark.end())
    }
  })
}

/** 通知服务器 **/
const mergeRequest = () =>{
  fetch('http://localhost:3000/merge',{
    method: 'POST',
    headers:{
    'content-type': 'application/json'
    },
    body:JSON.stringify({
      fileHash: fileHash.value,
      fileName: fileName.value,
      size:CHUNK_SIZE
    })
  }).then(res=>{
    console.log("合并成功")
  })
}

/** 上传文件 **/
const uploadChunks = async (chunks) => {
  //构造数组
  const data = chunks.map((chunk, index) => {
    return {
      fileHash: fileHash.value,
      chunkHash: fileHash.value + '-' + index,
      chunk: chunk
    }
  })

  //构造上传需要的formdata
  const formDatas = data.map((item) => {
    const formData = new FormData()
    formData.append('fileHash', item.fileHash)
    formData.append('chunkHash', item.chunkHash)
    formData.append('chunk', item.chunk)
    return formData
  })

  let max = 6 //最大请求数
  let index = 0
  const taskPool = [] //请求池
  while (index < formDatas.length) {
    const task = fetch('http://localhost:3000/upload', {
      method: 'POST',
      body: formDatas[index]
    })
    taskPool.splice(taskPool.findIndex(item => item === task))
    taskPool.push(task)
    if (taskPool.length === max) {
      await Promise.race(taskPool)
    }
    index++
  }
  await Promise.all(taskPool)
  //通知服务器合并请求
  mergeRequest()
}

/** 获取文件对象 **/
const handleUpload = async (e) => {
  let files = e.target.files;
  if (!files) return
  fileName.value = files[0].name
  //文件分片
  const chunks = createChunks(files[0])
  //hash计算
  const hash = await calculateHash(chunks)
  fileHash.value = hash
  console.log(hash)
  //上传分片
  uploadChunks(chunks)
}

</script>


<style scoped>

</style>
评论区
头像