• 基于element UI 实现大文件分片上传


    将文件进行切片,上传至服务器,上传完成后通知服务器进行合并

    屏幕录制2022-11-11 16.40.06

    • 测试用例
    <template>
      <div name="test-upload-img">
        <s-upload-img v-model="logo">s-upload-img>
      div>
    template>
    
    <script>
    export default {
      name: "test-upload-img",
      extends: {},
      mixins: [],
      components: {},
      emits: [],
      props: {},
      data() {
        return {
          logo: "zfs/2022-11-11/0ae8e4f733fe4bbdbb00f92fb1247273.jpeg"
        }
      },
      created() {
      },
      methods: {}
    }
    script>	
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • s-upload
    <script>
    import {getMinioUrl} from "../../../static/global.util";
    
    export default {
      name: "s-upload",
      extends: {mixins: []},
      components: {},
      emits: [],
      model: {},
      props: {
        value: {type: [String, Array]}, // v-model 绑定的Object
        limit: {type: Number, default: 1}, // 控制 上传图片数量
        accept: {type: String, default: undefined}, // 限制上传的文件类型
      },
      data() {
        return {
          fileList: [],
          chunkSize: 1024 * 1024 * 6, // 6M 为一个切片,超过6M,则采用上传大文件的方法进行文件上传
          percentage: {}, // 进度条
        }
      },
      computed: {
        action: {
          get() {
            return this.$getRequestUrl(this.$servers.openServer, 'tb/minio/uploadImgCompress')
          }
        },
        previewImgs: {
          get() {
            return this.fileList.map(o => getMinioUrl(o.url));
          },
        },
        hide: {
          get() {
            return this.fileList.length === this.limit;
          }
        }
      },
      watch: {},
      created() {
        this.fileList = (
            this.value
                ? this.limit === 1 ? [{url: this.value}] : (this.value instanceof Array ? this.value : JSON.parse(this.value)).map(o => ({url: o}))
                : []
        ).filter(o => o.url).map(({url}) => ({url, name: url.slice(url.lastIndexOf('/') + 1)})).map(o => ({...o, uid: o.name}));
      },
      methods: {
        getMinioUrl,
        onSuccess(res, file, fileList) {
          this.fileList = fileList;
          let imgs = this.limit === 1
              ? res.data.visitPath
              : JSON.stringify(fileList.map(img => img.response ? img.response.data.visitPath : img.url))
          console.log(imgs)
          this.$emit('input', imgs);
        },
        handleRemove(file) {
          this.fileList.splice(this.fileList.indexOf(file), 1)
          let imgs = this.limit === 1
              ? this.fileList.length <= 0
                  ? ''
                  : this.fileList.length === 1 && this.fileList[0].response
                      ? this.fileList[0].response.data.visitPath
                      : this.fileList[0].url
              : JSON.stringify(this.fileList.map(img => img.response ? img.response.data.visitPath : img.url))
          this.$emit('input', imgs);
        },
        httpRequest(params) {
          let {file: {size}} = params;
          size > this.chunkSize ? this.uploadBigFile(params, this.chunkSize) : this.uploadFile(params)
        },
        uploadFile(params) {
          let {file, filename, onSuccess} = params;
          const formData = new FormData();
          formData.append(filename, file);
          const config = {
            'Content-type': 'multipart/form-data',
            onUploadProgress: progress => this.percentage = Math.floor(progress.loaded / progress.total * 100)
          }
          this.$post(this.$servers.openServer, 'tb/minio/upload', formData, config).then(onSuccess)
        },
        calculationPercentage(file, index, loadeds, size) {
          const loaded = loadeds.filter(o => o.length > 0).map(o => o[o.length - 1]).reduce((prev, curr) => prev + curr)
          this.$set(this.percentage, file.uid, Math.floor(loaded / size * 100))
        },
        uploadBigFile(params, chunkSize) {
          let {file, filename, onSuccess} = params;
          let {size, type, name} = file;
          const chunkLength = Math.ceil(file.size / chunkSize)
          let chunks = Array.from({length: chunkLength}).map((v, i) => file.slice(i * chunkSize, i * chunkSize + chunkSize))
          let loadeds = [];
          let chunkRequests = chunks.map((chunk, i) => {
            const formData = new FormData();
            formData.append(filename, chunk);
            loadeds[i] = [];
            const config = {
              'Content-type': 'multipart/form-data',
              onUploadProgress: progress => {
                loadeds[i].push(progress.loaded)
                this.calculationPercentage(file, i, loadeds, size);
              }
            }
            return this.$post(this.$servers.openServer, 'tb/minio/upload', formData, config);
          })
          this.$axios.all(chunkRequests).then(res => {
            let fileNames = res.map(({data: {minioPath}}) => minioPath)
            const params = {fileName: name, contentType: type, fileSize: size, fileNames}
            this.$post(this.$servers.openServer, 'tb/minio/merge', params).then(onSuccess)
          })
        },
        onRemove(file, fileList) {
          this.percentage = -1;
        }
      }
    }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • s-upload-img
    <style scoped lang="less">
    .hide-upload {
      /deep/ .el-upload {
        visibility: hidden;
      }
    }
    style>
    <template>
      <div name="s-upload-img">
        <el-upload
            :class="{'hide-upload':hide}" list-type="picture-card" v-bind="$attrs"
            :file-list="fileList" :action="action" :limit="limit" :accept="accept"
            :on-success="onSuccess" :http-request="httpRequest">
          <i slot="default" class="el-icon-plus">i>
          <div slot="file" slot-scope="{file}">
            <el-progress v-if="percentage[file.uid] && percentage[file.uid]<100" type="circle" :percentage="percentage[file.uid]">el-progress>
            <img :src="getMinioUrl(file.url)" style="width:100%; height:auto;" alt="">
            <span class="el-upload-list__item-actions">
              <span class="el-upload-list__item-preview" @click="()=>this.$refs.previewImg.showViewer = true">
                <i class="el-icon-zoom-in">i>
              span>
              <span class="el-upload-list__item-delete" @click="handleRemove(file)">
                <i class="el-icon-delete">i>
              span>
            span>
          div>
        el-upload>
        <el-image ref="previewImg" v-show="false" src :preview-src-list='previewImgs'>el-image>
      div>
    template>
    
    <script>
    import SUpload from "./s-upload";
    
    export default {
      name: "s-upload-img",
      extends: SUpload,
      props: {
        accept: {type: String, default: '.jpeg,.jpg,.png'}, // 限制上传的图片类型
      },
    }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
  • 相关阅读:
    ubuntu安装mysql详细过程
    ARouter原理解析之路由跳转
    【React】React 项目启动卡在Starting the development server...的问题
    [每周一更]-(第21期):什么是RPC?
    多线程与多进程区别
    抖音作品评论id提取工具|视频内容提取软件
    以下关于服务器控件的叙述中正确的是
    C++ 什么时候用`.template` 和 `::template`
    OpManager 帮助排查网络延迟问题
    计算机毕业设计django基于python教学互动系统(源码+系统+mysql数据库+Lw文档)
  • 原文地址:https://blog.csdn.net/qq_33547169/article/details/127807333