大文件上传:Client客户端

该代码示例展示了如何在Vue3应用中使用ElementPlus组件库和Axios库实现文件上传功能,特别是断点续传和进度条展示。文件被切片并逐个发送到服务器,同时更新上传进度。当所有切片上传完成后,文件在服务器端合并成原始文件。
摘要由CSDN通过智能技术生成

这里不做过多赘述,代码都有相应的注释,作为简单的demo,这里直接贴上代码,这里使用的是vue3

<script setup>
import { UploadFilled } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { ref } from 'vue'
import { fileParse } from './assets/utils'
import sparkMD5 from 'spark-md5'
import axios from 'axios'
// 图片路径
const img = ref(null)
// 状态
const btn = ref("暂停")
const btnStatus = ref(false)
// total
const total = ref(0)
// 显示用total
const viewTotal = ref(0)
// 外层使用partList
const outerPartList = ref([])
// 外层的hash
const outerHash = ref([])
// vieo路径
const video = ref("")
// 当前传入的文件大小
const outerSize = ref(0)

const open = (text) => {
  ElMessage(text)
}

// const totalText = (total) => {
//   return total > 100 ? 100 : total
// }

const changeFile = async (file) => {
  if(!file) return

  // 解析为BUFFER数据
  // 我们会把文件切片处理:把一个文件分割成为好几个部分(固定数量/固定大小)
  // 每一个切片有自己的部分数据和自己的名字
  // HASH名字
  // HASH-1.mp4
  // HASH-2.mp4
  // ...
  let buffer = await fileParse(file, "buffer"),
    spark = new sparkMD5.ArrayBuffer(),
    hash,
    suffix
  spark.append(buffer)
  hash = spark.end()
  suffix = /\.([0-9a-zA-Z]+)$/i.exec(file.raw.name)[1] // 取出扩展名

  // 创建切片
  let {size} = file.raw
  let partList = [],
    partSize = 1 * 1024 * 1024,
    partTotal = Number.isInteger(size / partSize) ? size / partSize : parseInt(size / partSize) + 1,
    cur = 0
  // 将当前的值赋给外部
  outerSize.value = partTotal

  for(let i = 1; i <= partTotal; i++) {
    let item = {
      chunk: (i != partTotal) ? file.raw.slice(cur, cur + partSize) : file.raw.slice(cur, size),
      filename: `${hash}_${i}.${suffix}`
    }
    cur += partSize
    partList.push(item)
  }

  outerPartList.value = partList
  outerHash.value = hash
  console.log("输出当前outerPartList", outerPartList.value)
  sendRequest()
}

const sendRequest = async () => {
  // 根据当前切片数量创造对应数量的请求(集合)
  let requestList = []
  console.log(outerPartList)
  outerPartList.value.forEach((item, index) => {
    // 每一个函数的都是发送一个切片的请求
    let fn = () => {
      let formData = new FormData()
      formData.append('chunk', item.chunk)
      formData.append('filename', item.filename)

      return axios.post(
        '/api/single3',
        formData,
        {headers:
          {"Content-Type": "multipart/form-data"}
        }
      ).then(result => {
        result = result.data
        if(result.code ==0) {
          total.value += 1
          // 计算当前的百分比
          viewTotal.value = parseInt((total.value / outerSize.value) * 100)
          // 传完的切片我们把它移除掉
          outerPartList.value.splice(0, 1)
        }
      })
    }
    requestList.push(fn)
  })

  // 传递:并发(ajax.abort()强行中断)/串行(基于标识控制不发送)
  let i = 0
  let complete = async () => {
    let result = await axios.get('/api/merge', {
      params: {
        hash: outerHash.value
      }
    })
    result = result.data
    if(result.code == 0) {
      video.value = result.path
    }
  }
  let send = async () => {
    if(btnStatus.value) {
      return
    }
    if(i >= requestList.length) {
      // 都传输完毕
      complete()
      return
    }
    await requestList[i]()
    i++
    send()
  }

  send();
}

const handleBtn = () => {
  if(btn.value == "继续") {
    // 断点续传
    btn.value = "暂停"
    btnStatus.value = false
    sendRequest()
    return
  }
  btn.value = "继续"
  btnStatus.value = true
  // 暂停上传

}

</script>

<template>
  <header>
    <!-- 
      action: 存放的时文件上传到服务器的接口地址

     -->
    <el-upload
    class="upload-demo"
    drag
    action
    :auto-upload="false"
    :show-file-list="false"
    :on-change="changeFile"
    multiple
  >
    <el-icon class="el-icon--upload"><upload-filled /></el-icon>
    <div class="el-upload__text">
      Drop file here or <em>click to upload</em>
    </div>
    <template #tip>
      <div class="el-upload__tip">
        jpg/png files with a size less than 500kb
      </div>
    </template>
  </el-upload>
  </header>

  <div class="progress">
    <span>上传进度:{{ viewTotal }}%</span>
    <el-link type="primary" v-if="viewTotal>0 && viewTotal<100" @click="handleBtn">{{ btn }}</el-link>
  </div>

  <!-- IMG -->
  <div class="uploadImg" v-show="img">
    <img :src="img" alt="">
  </div>

  <!-- VIDEO -->
  <div class="uploadImg" v-if="video">
    <video :src="video" controls></video>
  </div>
</template>

<style scoped>
  .uploadImg {
    width: 100%;
  }
  .uploadImg video {
    width: 100%;
  }
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值