cos-js-sdk-v5安装使用

首选安装cos-js-sdk-v5

npm install cos-js-sdk-v5

定义BucketRegion

// 桶地址
// config/index.js
export const bucket = {
  Bucket: 'bio-assay-你自己项目的',
  Region: '****你自己项目的****'
}

定义请求方法

我们在项目中封装了http请求的公共方法,同时为看安全起见,将腾讯云的关键信息存储在远程服务器,需要上传文件时,先调用请求获取关键参数。

// 获取上传文件token
const getCosAuth =  function (params) {
    return new Promise((resolve, reject) => {
        Vue.req.get('api/****/****', { options}).then((res) => {
            resolve(res)
        }).catch((err) => {
            reject(err)
        })
    })
}

取消请求

有时候我们需要取消正在上传的文件,则需要一个取消请求的方法

// 请求的集合
export const promiseCancel = {
  cancel: []
}
// 取消请求方法
export const onCancel = (url, message = '') => {
  promiseCancel.cancel.forEach((item) => {
    if (item.url === url) {
      item.cancel && item.cancel(message || item.url)
      promiseCancel.cancel.splice(item, 1)
    }
  })
}

利用MD5生成桶的key

为了保证文件上传的唯一性,我们将根据文件的内容生成唯一的key值

首先需要安装spark-md5

npm install spark-md5
// getFileMD5.js
import SparkMD5 from 'spark-md5'

function getFileMD5 (file, callback) {
  const chunkSize = 1024 * 1024 * 2
  const chunks = Math.ceil(file.size / chunkSize)
  let currentChunk = 0
  const spark = new SparkMD5.ArrayBuffer()
  const fileReader = new FileReader()

  fileReader.onload = (e) => {
    spark.append(e.target.result)
    currentChunk++
    if (currentChunk < chunks) {
      loadNext()
    } else {
      callback(spark.end())
    }
  }

  function loadNext () {
    const start = currentChunk * chunkSize
    const end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize
    fileReader.readAsArrayBuffer(file.slice(start, end))
  }

  loadNext()
}

export default getFileMD5

完善方法

现在我们来完善一下整体的方法

// upload/index.js
import COS from 'cos-js-sdk-v5'
import getFileMD5 from './getFileMD5'
import { bucket } from './config/index'
import { onCancel } from './http/promiseCancel'
import Vue from 'vue'

export class UploadFile {
  constructor () {
    this.bucketName = bucket.Bucket
    // 监听文件上传的进度,默认是一个空函数
    this.onUploadProgress = () => {}
    // 桶的实例化
    this.cosObj = {}
    // 文件上传的id值
    this.cTaskId = ''
    // 请求token的唯一id值
    this.currentHttpId = ''
    // 取消请求的信息,此处可以忽略
    this.cancelMessage = 'needCancel'
  }

  // 获取上传文件token
  getCosAuth (params) {
    return new Promise((resolve, reject) => {
      Vue.req.get('api/**/**', { params: params, concelkey: this.currentHttpId }).then((res) => {
        resolve(res)
      }).catch((err) => {
        reject(err)
      })
    })
  }

  cancelUpload (msg = 'needCancel') {
    this.cancelMessage = msg
    // 取消获取token
    onCancel(this.currentHttpId, this.cancelMessage)
    // 取消文件上传的请求
    this.cosObj.cancelTask && this.cosObj.cancelTask(this.cTaskId)
  }
  // 文件转成MD5

  fileToMD5 (file, resolve, reject) {
    // 获取文件的md5
    getFileMD5(file, (md5) => {
      const subFix = file.name.substr(file.name.lastIndexOf('.'))
      const key = md5 + subFix
    //   上传到腾讯云
      this.putObject(key, file, resolve, reject)
    })
  }

  // 开始上传文件
  startUpload (file, { progressFn } = {}) {
    // 文件上传的进度监听函数
    this.onUploadProgress = progressFn
    // 生成当前请求的唯一id值
    this.currentHttpId = 'http' + new Date().getTime() + Math.random()
    return new Promise((resolve, reject) => {
      this.getCosAuth({ bucketName: this.bucketName }).then((res) => {
        if (res.actionResult === '1') {
          if (!res.data || !res.data.Credentials) {
            reject(new Error('credentials invalid'))
          } else {
            const _data = res.data.Credentials
            const cosAuth = {
              TmpSecretId: _data.TmpSecretId,
              TmpSecretKey: _data.TmpSecretKey,
              XCosSecurityToken: _data.Token,
              StartTime: res.data.StartTime,
              ExpiredTime: res.data.ExpiredTime,
              ScopeLimit: true
            }
            const AUTH = 'getAuthorization'
            // 保存一下实例
            this.cosObj = new COS({
              [AUTH] (options, callback) {
                callback(cosAuth)
              }
            })
            // 生成md5
            this.fileToMD5(file, resolve, reject)
          }
        } else {
          reject(new Error(res.message))
        }
      }).catch((err) => {
        reject(err)
      })
    })
  }

  // 上传腾讯云
  putObject (key, file, resolve, reject) {
    this.cosObj.putObject({
      Bucket: bucket.Bucket,
      Region: bucket.Region,
      Key: key,
      Body: file,
      onProgress: progressData => {
        // 上传进度条的回调函数
        this.onUploadProgress && this.onUploadProgress(progressData)
      },
      onTaskReady: taskId => {
        this.cTaskId = taskId
      }
    }, (err, data) => {
      if (err) {
        reject(new Error(err.error.Code))
      } else {
        resolve(data)
      }
    })
    // 监听文件上传更新状态
    this.cosObj.on('list-update', (data) => {
      if (data && data.list && data.list[0].state === 'canceled') {
        const errMessage = { message: this.cancelMessage }
        reject(errMessage)
      }
    })
  }
}

以上就是整体的封装方法

组件中使用

现在需要定义一个公共的文件上传组件

改组件依赖了vue的ant-design-vueUI组件库

<template>
  <div>
    <div v-show="isShowUploadBtn" v-if="showUpload" class="upload-warp">
      <a-upload
        name="file"
        ref="upload"
        :multiple="false"
        :disabled="disabled"
        :show-upload-list="false"
        :customRequest="customRequest"
        :accept="accept.join(',')"
        class="upload-list-inline"
        listType="picture">
        <a-button class="upload-btn">
          <my-icon type="iconupload"></my-icon> {{BtnName}}
        </a-button>
      </a-upload>
      <span class="ext-name" v-if="isShowExtNname">支持扩展名:{{ extNname }}</span>
    </div>
    <div v-if="isShowProgress" style="width:90%;" class="progress-warp">
      <p class="t-overflow"><a-icon type="paper-clip" /> {{currentFileName}}</p>
      <div class="progress">
        <a-progress
          :stroke-color="{
            '0%': '#2FC6A3',
            '100%': '#2FC6A3',
          }"
          :percent="percentValue"
        />
        <my-icon type="iconashbin" class="remove" title="点击终止上传" @click="cancelUploadHandler"></my-icon>
      </div>
    </div>
  </div>
</template>
  import { Upload, Icon, Progress } from 'ant-design-vue'
  import { UploadFile } from './index'
  export default {
    name: 'UploadFile',
    components: {
      [Icon.name]: Icon,
      [Progress.name]: Progress,
      [Upload.name]: Upload
    },
    props: {
      accept: {
        type: Array,
        required: false,
        default: () => ['.doc', '.docx']
      },
      disabled: {
        type: Boolean,
        default: false
      },
      files: {
        type: Array,
        default: () => []
      },
      listLenght: {
        type: Number,
        default: 10
      },
      BtnName: {
        type: String,
        default: '上传文件'
      },
      isShowUploadBtn: {
        type: Boolean,
        default: true
      },
      isShowExtNname: {
        type: Boolean,
        default: true
      },
      // 文件上传大小限制
      limtSize: {
        type: Number,
        default: 50
      }
    },
    data () {
      return {
        percentValue: 0,
        currentFileName: '',
        isUploading: false,
        uploadInstance: {}
      }
    },
    computed: {
      showUpload () {
        // 如果是列表上传,则上传文件个数不大最大值,则显示上传按钮
        if (!this.isUploading && this.fileList.length < this.listLenght) {
          return true
        }
        if (!this.fileList.length === 0 && !this.isUploading) {
          return true
        }
        return false
      },
      isShowProgress () {
        return this.isUploading
      }
    },
    methods: {
      // 上传文件 返回腾讯云信息,通过事件将存储信息返回到父组件
      customRequest (info) {
        // console.log(info)
        const { file } = info
        // console.log("handleUpload",file)
        const size = (file.size / 1024 / 1024).toFixed(2)
        this.currentFileName = file.name
        const extName = this.getExtName(file.name)
        const { accept } = this
        this.isUploading = true
        if (accept.length && !accept.includes(`.${extName}`.toLowerCase())) {
          this.$message.error('文件格式错误')
          this.isUploading = false
          return
        }
        if (size > this.limtSize) {
          this.$message.error('上传文件不能大于' + this.limtSize + 'M')
          this.isUploading = false
          return
        }
        this.uploadInstance = new UploadFile()
        this.uploadInstance.startUpload(file, {
          progressFn: this.progress
        }).then(res => {
          if (res.statusCode === 200) {
            this.isUploading = false
            this.percentValue = 0
            this.$emit('fileList', {
              uid: file.uid,
              name: this.currentFileName,
              url: 'https://' + res.Location
            })
          }
        }).catch((err) => {
          this.percentValue = 0
          this.isUploading = false
          if (err && err.message === 'needCancel') {
            this.$message.warning('文件上传已取消!')
          } else {
            this.$message.warning(err.message || err || '上传异常!')
          }
        })
      },
      // 监听文件上传的进度
      progress (data) {
        this.percentValue = data.percent ? +(data.percent * 100).toFixed(2) : 0
      },
      cancelUploadHandler () {
        // 取消文件上传
        this.uploadInstance.cancelUpload && this.uploadInstance.cancelUpload()
      }
    }
}

以上代码仅显示了文件上传的关键逻辑,如果想拓展功能,可以自行添加代码

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐