大文件切片上传

封装成通用组件:

<template>
  <div id="app">
    <!-- 上传组件 -->
    <el-upload
      action=""
      :auto-upload="false"
      :show-file-list="false"
      :on-change="handleChange">
      <el-button size="small" type="primary">点击上传</el-button>
      <div slot="tip" class="el-upload__tip">{{ placeholder }}</div>
    </el-upload>
    <el-dialog
      title="提示"
      :visible.sync="dialog"
      width="400px"
      center>
      <div class="display-flex fd-column ai-center">
        <p class="mt-0">关闭弹窗后可继续上传,不推荐关闭,以免文件传输失败</p>
        <el-progress type="circle" :percentage="Number(percent.toFixed())"></el-progress>
        <div class="mt-20">
          <el-button type="primary" size="mini" @click="handleClickBtn">{{ upload | btnTextFilter}}</el-button>
          <el-button type="warning" size="mini" @click="handleClose">取消上传</el-button>
        </div>
      </div>
    </el-dialog>
    <div v-for="(item, index) in fileList" :key="index" class="display-flex ai-center jc-space-between fs-14 mt-6 cursor-pointer" :style="{width: fileWidth}">
      <div class="display-flex ai-center" style="width: 90%;">
        <i class="el-icon-document"></i>
        <span class="ml-4 text-ellipsis" style="width: 90%;">{{item.name}}</span>
      </div>
      <i class="el-icon-close" @click="delFile(item)"></i>
    </div>
  </div>
</template>

<script>
import axios from 'axios'

export default {
  name: 'BigFileUpload',
  props: {
    placeholder: {
      type: String,
      default () {
        return '可上传多个文件,不限制格式'
      }
    },
    folder: {
      type: String,
      default () {
        return 'test'
      }
    },
    fileWidth: {
      type: String,
      default () {
        return '300px'
      }
    }
  },
  filters: {
    btnTextFilter (val) {
      return val ? '暂停上传' : '继续上传'
    }
  },
  watch: {
    fileList: {
      handler: function (value) {
        this.$emit('change', value)
      },
      deep: true
    }
  },
  data () {
    return {
      dialog: false,
      fileList: [],
      index: 0, // 当前传输位置
      uploadId: null,
      totalChunks: 0,
      percent: 0,
      upload: false,
      percentCount: 0,
      chunkList: []
    }
  },
  methods: {
    handleClose () {
      this.$confirm('此操作将取消并关闭上传此文件, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        const data = {
          folder: this.folder,
          uploadId: this.uploadId
        }
        axios.delete('/big/file/delete/oss/part', {
          data: data
        }).then(() => {
          this.$message.success('取消成功')
          this.upload = false
          this.dialog = false
        })
      }).catch(() => {
      });
    },
    delFile (file) {
      this.$confirm('此操作将永久删除该数据, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        const data = {
          filenames: [file.url],
          folder: this.folder
        }
        axios.delete('/big/file/delete/oss/file', {
          data: data
        }).then(() => {
          this.fileList = this.fileList.filter(item => item.url !== file.url)
          this.$message.success('删除成功')
        })
      }).catch(() => {
      });
    },
    handleChange (file) {
      this.upload = true
      this.percent = 0
      this.percentCount = 0
      this.index = 0
      this.chunkList = []
      this.filename = file.name
      axios.post('/big/file/initUpload', {
        filename: this.filename,
        folder: this.folder
      }).then(res => {
        this.dialog = true
        this.uploadId = res.data.data
        // 获取文件并转成 ArrayBuffer 对象
        const fileObj = file.raw
        // 将文件按固定大小(5M)进行切片,注意此处同时声明了多个常量
        const chunkSize = 5242880;
        const chunkList = []; // 保存所有切片的数组
        const chunkListLength = Math.ceil(fileObj.size / chunkSize); // 计算总共多个切片
        // 生成切片,这里后端要求传递的参数为字节数据块(chunk)和每个数据块的文件名(fileName)
        let curChunk = 0 // 切片时的初始位置
        for (let i = 0; i < chunkListLength; i++) {
          const item = {
            index: i,
            chunk: fileObj.slice(curChunk, curChunk + chunkSize)
          }
          curChunk += chunkSize
          chunkList.push(item)
        }
        this.totalChunks = chunkListLength
        this.chunkList = chunkList // sendRequest 要用到
        this.sendRequest()
      })
    },

    // 发送请求
    sendRequest () {
      const requestList = [] // 请求集合
      if (this.index) {
        this.chunkList = this.chunkList.filter(item => item.index > this.index)
      }
      this.chunkList.forEach(item => {
        const fn = () => {
          return new Promise((resolve, reject) => {
            const formData = new FormData()
            formData.append('file', item.chunk)
            formData.append('index', item.index)
            formData.append('uploadId', this.uploadId)
            return axios({
              url: '/big/file/uploadChunk',
              method: 'post',
              headers: {
                'Content-Type': 'multipart/form-data',
                Authorization: this.$store.state.token,
                tenant_id: this.$cookies.get('tenant_id') || undefined
              },
              timeout: 100000,
              data: formData
            }).then(res => {
              if (res.data.code === 200) {
                resolve(res)
                this.index = item.index
                if (this.percentCount === 0) { // 避免上传成功后会删除切片改变 chunkList 的长度影响到 percentCount 的值
                  this.percentCount = 100 / this.chunkList.length
                }
                this.percent += this.percentCount // 改变进度
              } else {
                reject(res)
              }
            }).catch((err) => {
              reject(err)
              this.upload = false
            })
          })
        }
        requestList.push(fn)
      })
      console.log(requestList, 11111111111111)
      let i = 0 // 记录发送的请求个数
      const complete = () => {
        axios({
          url: `/big/file/mergeFile?uploadId=${this.uploadId}`,
          method: 'post',
          headers: {
            Authorization: this.$store.state.token,
            tenant_id: this.$cookies.get('tenant_id') || undefined
          }
        }).then(res => {
          this.fileList.push({
            name: this.filename,
            url: res.data.data
          })
          this.dialog = false
        })
      }
      const send = async () => {
        if (!this.upload) return
        if (i >= requestList.length) {
          // 发送完毕
          complete()
          return
        }
        await requestList[i]()
        i++
        send()
      }
      send() // 发送请求
    },
    // 按下暂停按钮
    handleClickBtn () {
      this.upload = !this.upload
      // 如果不暂停则继续上传
      if (this.upload) this.sendRequest()
    }
  }
}
</script>

<style scoped lang="scss">
</style>

引入使用:

import BigFileUpload from '@/components/BigFileUpload';
<big-file-upload folder="report" @change="data => drawerAddForm.file = data"></big-file-upload>

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值