一篇文章搞定阿里云分片上传大文件多文件

914 阅读2分钟

前言

项目上传文件之前是阿里云直传的方式,因上传一个文件就要1GB,所以研究了一下分片上传模式!阿里云上单个大文件分片上传的API文档写的非常清楚,在此给大家安排一下多文件大文件上传,以下引用阿里云的一段话!

当需要上传的文件较大时,可以通过MultipartUpload接口进行分片上传。分片上传是指将要上传的文件分成多个数据块(Part)来分别上传。当其中一些分片上传失败后,OSS将保留上传进度记录,再次重传时只需要上传失败的分片,而不需要重新上传整个文件。一般大于100 MB的文件,建议采用分片上传的方法,通过断点续传和重试,提高上传成功率。

1、oss-sectioning.js

文件配置

import Vue from 'vue
function infoClient () {  
    let OSS = require('ali-oss')  
    let ossConfig = {    
        region: 'xxx',    
        // 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。    
        //   强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录RAM控制台创建RAM账号。    
        accessKeyId: 'xxx',    
        accessKeySecret: 'xxx',    
        bucket: 'xxx'  
    }  
    return new OSS(ossConfig)
}
let tempCheckpointObj = {}

上传文件、断点恢复上传、暂停取消上传

export default class OssSectionUpload {
  constructor (fileName, file, url) {
    this.fileName = fileName
    this.file = file
    this.url = url
    this.client = infoClient()
  }
  // 上传文件
  async multipartUpload (callback) {
    try {
      // object-key可以自定义为文件名(例如file.txt)或目录(例如abc/test/file.txt)的形式,实现将文件上传至当前Bucket或Bucket下的指定目录。
      let that = this
      await this.client.multipartUpload(this.url, this.file, {
        parallel: 4,
        partSize: 1000 * 1024,
        progress: function (p, checkpoint) {
          // 断点记录点。浏览器重启后无法直接继续上传,您需要手动触发上传操作。
          tempCheckpointObj[that.fileName] = checkpoint
          callback(p, that.fileName)
        },
        meta: { year: (new Date()).getFullYear(), people: Vue.prototype.$auth.user().userName }
      }).then(res => {
        Vue.prototype.$message.success('上传成功')
      }).catch(() => {
        if (this.client.isCancel()) {
          Vue.prototype.$message.error('上传失败')
        } else {
          Vue.prototype.$message.error('网络异常')
        }
      })
    } catch (e) {
      console.log(e)
    }  }  
// 恢复上传
  async resumeUpload (callback) {
    try {
      let that = this
      await this.client.multipartUpload(this.fileName, this.file, {
        parallel: 4,
        partSize: 1000 * 1024,
        progress: function (p, checkpoint) {
          this.tempCheckpoint = checkpoint
          callback(p)
        },
        checkpoint: tempCheckpointObj[that.fileName],
        meta: { year: (new Date()).getFullYear(), people: Vue.prototype.$auth.user().userName }
      }).then(res => {
        Vue.prototype.$message.success('上传成功')
      }).catch(() => {
        if (this.client.isCancel()) {
          Vue.prototype.$message.error('上传失败')
        } else {
          Vue.prototype.$message.error('网络异常')
        }
      })
    } catch (e) {
      console.log(e)
    }
  }
  // 取消上传
  cancleUpload () {
    this.client.cancel()
  }}

2、vue + element-ui 

<template>
 <div :class="$style.upload">
     <el-upload
      ref='upload'
      action='aaa'
      :show-file-list="true"
      :file-list="playUrls"
      :http-request="uploadSectionFile"
      :on-remove="onRemove"
      :multiple="multiple"
      :on-preview="handlePreview">
        <el-button size="small" type="primary" >{{btnText}}</el-button>
    </el-upload>
 </div>
</template>

<script>
import OssSectionUpload from '@/utils/oss-sectioning'
export default {
  name: 'pt-upload-big-file',
  props: {
    objectId: {
      type: String,
      default: ''
    },
    value: {
      type: [Array, Object, String],
      default: function () {
        return []
      }
    },
    resourceConfig: {
      type: String,
      default: ''
    },
    isPublic: {
      type: Boolean,
      default: false
    },
    multiple: {
      type: Boolean,
      default: false
    },
    btnText: {
      type: String,
      default: '点击上传'
    }
  },
  data () {
    return {
      progress: 0,
      playUrls: [],
      currentValue: this.getValue(),
      isUpload: false
    }
  },
  computed: {
    resourceUrl () {
      return this.isPublic ? this.$publicResourcesUrl : this.$privateResourcesUrl
    }
  },
  watch: {
    value: {
      handler: function (val) {
        if (val && JSON.stringify(val) !== '{}') {
          this.currentValue = val
        }
      },
      immediate: true
    },
    isUpload (val) {
      this.$emit('isUpload', val)
    }
  },
  methods: {
    handlePreview (file) {
      if (this.playUrls.every(item => item.percentage === 100)) {
        this.$message.info('已全部上传完毕')
        return
      }
      this.playUrls.forEach((ele, idx) => {
        if (ele.name === file.name) {
          if (ele.continueUpload) {
            this.$message.info('恢复上传')
            ele.client.resumeUpload((p) => {
              this.playUrls[idx].percentage = parseFloat((p * 100))
              if (this.playUrls.every(item => item.percentage === 100)) {
                this.isUpload = true
              }
            })
          } else {
            this.$message.info('暂停上传')
            ele.client.cancleUpload()
          }
          ele.continueUpload = !ele.continueUpload
        }
      })
    },
    getValue () {
      return this.multiple ? [] : {}
    },
    dealUploadData (params, url) {
      let func = new OssSectionUpload(changeFileName, params.file, url)
      this.playUrls.push({
        url: this.resourceConfig + '/' + this.objectId + '/' + params.file.name,
        name: params.file.name,        client: func,        status: 'uploading',
        percentage: 0,
        continueUpload: false
      })
      func.multipartUpload((p, fileName) => {
        this.playUrls.forEach((ele, idx) => {
          if (fileName === ele.name) {
            this.playUrls[idx].percentage = parseFloat((p * 100))
          }
        })
        if (this.playUrls.every(item => item.percentage === 100)) {
          this.isUpload = true
        }
      })
    },
    async uploadSectionFile (params) {
      let url = this.resourceConfig + '/' + this.objectId + '/' + params.file.name
      this.dealUploadData(params, url)
      let data = {
        url,
        name: params.file.name
      }
      if (this.multiple) {
        this.currentValue.push(data)
      } else {
        this.currentValue = data
      }
      this.$emit('input', this.currentValue)
    },
    clearFiles () {
      this.$refs.upload.clearFiles()
    },
    onRemove (file, fileList) {
      if (this.multiple) {
        for (let i = 0; i < this.playUrls.length; i++) {
          if (this.playUrls[i].name === file.name) {
            this.playUrls[i].client.cancleUpload()
            this.currentValue.splice(i, 1)
            this.playUrls.splice(i, 1)
            break
          }
        }
        this.$emit('input', this.currentValue)
      } else {
        this.$emit('input', '')
        this.playUrls[0].client.cancleUpload()
        this.playUrls = []
      }
    }
  }}
</script>
<style lang="less" module>
.upload{
  :global {
    :focus{
      outline:none;
    }
    .el-upload{
      width: 60%;
      text-align: left;
    }
  }}
</style>

总结

希望对正在看要完成这个需求的你有所帮助,也请各位大佬有建议多多提出来,感谢