基于vue3,el-upload实现多文件切片上传

基于vue3,el-upload实现多文件切片上传

原理:将大文件拆分,通过异步的方式发送给服务端。
1.将文件切片

function sliceFile() {
  const chunkSize = 10 * 1024 * 1024 //设置切片的大小,这里为10M
  chunkCount.value = Math.ceil(selectedFile.value.size / chunkSize) // 计算文件切片总数
  chunkCountList.value.push(chunkCount.value) // 用一个数组保存,一个文件切出来的总数
  for(let i = 0; i < chunkCount.value; i++) {
    const chunk = selectedFile.value.slice(i * chunkSize, (i + 1) * chunkSize) // 获取切片
    requestPool.value.push({ chunk, index: i }) // 加入请求池
  }
  processPool() // 处理请求池
}

2.处理请求池中的切片上传

function processPool() {
  while(requestPool.value.length > 0 && MAX_REQUEST > 0) { // MAX_REQUEST 设置的最大上传接口请求数
    const { chunk, index } = requestPool.value.shift() // 取出一个切片
    uploadChunk(chunk, index).then(() => { // 真正的上传接口切片
      if (requestPool.value.length > 0) {
        processPool() // 继续处理请求池
      }
    }).finally(() => {
      MAX_REQUEST++ // 释放一个请求槽
    })
    MAX_REQUEST-- // 占用一个请求槽
  }
}

3.文件上传的具体接口

async function uploadChunk(chunk, index) {
  if (index === 0 ) i.value++ //多文件上传区分文件,index === 0 意味着一个文件切片上传完了,开始新的文件,准确获取到文件名,跟对应的总切片数
  const formData = new FormData() // 创建一个formData对象
  formData.append('file', chunk) // 切片后的具体文件
  formData.append('file_name', nameList.value[i]) //切片对应的文件名称
  formData.append('total_chunks', chunkCountList.value[i.value]) // 文件对应的总切片
  formData.append('chunk_index', index) // 切片对应的序号
  await axios.post('xxxx', formData, {
    headers: {
      'Content-Type': 'multipart/form-data', // 这里content-type类型要定义好,跟常规的数据请求接口不同,很重要!!!
      'Authorization': 'Bearer ' + getToken(), // 这里设置token
    }
  })
}

4.文件选择处理方法

async function handleFileChange(event, file) {
  // 防止文件重复选择
  const obj = uploadFileList.value.find(it => it.name === event.name)
  if (!obj) uploadFileList.value.push(event)
  await onVerity(uploadFileList.value) // 这里是校验文件,通过后端接口校验,有一个小技巧,这里用防抖,多个文件只会统一校验一次
}
const onVerity = Debounce(async (file) => {
   const verityList = file.map(it => {
      return {
        video_file_type: it.name.split('_')[0],
        video_file_name: it.name
      }
    })
    await uploadVerifyNameApi({ videos: verityList, patrol_id: props.data.patrol_id }).then(rsp => {
      dataSource.value = rsp || []
      emit('editStatus', dataSource.value.filter(it => { return it.success }))
    })
}, 500)

5.提交方法

function submit() {
  isLoading.value = true // 按钮loading
  // 过滤掉失败的文件
  filterFailureFile(dataSource.value) // 这个方法自己视业务逻辑书写
  chunkCountList.value = [] //重置数据
  nameList.value = [] //重置数据
  i.value = -1 //重置数据
  uploadFileList.value.forEach(async it => { //uploadFileList是需要上传的文件
    selectedFile.value = it.raw
    nameList.value.push(selectedFile.value.name) // 储存好文件的名称,后续好匹配上传切片
    sliceFile() // 切片并上传
  })
  createInterval() // 开启文件上传进度轮询
}

6.定义的一些变量

let selectedFile = ref(null) // 选择的文件
let requestPool = ref([]) // 文件请求池
let MAX_REQUEST = 10 // 最大请求数
let chunkCount = ref(0) // 单个切片总数
const fileList = ref([]) // el-upload 绑定的数组
const uploadFileList = ref([]) // 需要上传的文件列表
let chunkCountList = ref([]) // 选择的文件切片总数数组
let nameList = ref([]) // 选择的文件名称数组
let i = ref(-1) // 为了筛选出具体的文件名称及切片总数
还有一些其他业务内,不是很重要的没有贴出来

7.html代码

// 有一些删减,大致就是这样了
<template>
  <el-upload
    :on-change="handleFileChange"
    :file-list="fileList"
    :auto-upload="false"
    :drag="true"
    :show-file-list="false"
    class="upload-slice"
    multiple
  >
    <div class="upload-slice-info">
      <div class="info">请点击选择文件,或将<span style="color: red;">文件</span>放置框内</div>
    </div>
  </el-upload>
</template>

参考文章1
参考文章2

你可能感兴趣的:(前端,javascript,vue.js)