antdv的Upload组件实现前端压缩图片并自定义上传功能

Ant Design of Vue的Upload组件有几个重要的api属性:

beforeUpload: 上传文件之前的钩子函数,支持返回一个Promise对象。

customRequest: 覆盖组件默认的上传行为,实现自定义的上传请求。

功能实现原理

在上传图片前获取该图片的文件流(beforeUpload中获取),对这个文件流进行压缩操作,再将压缩后的文件流传过去(resolve(newFile)),最后进行自定义的上传请求(customRequest中实现)。

图片预览方式

前端本地图片的预览则可以选择blob或者base64的方式,本文推荐使用blob方式来预览图片。

部分代码

html

<template>
  <div>
    <a-spin :spinning="isShowSpinning">
      <img
        v-if="imageUrl"
        :src="imageUrl"
        style="width: 200px; height: 200px; margin-right: 10px;"
      />
      <a-upload
        accept="image/*"
        :beforeUpload="beforeImageUpload"
        :customRequest="customImageRequest"
      >
        <a-button type="primary">
          <a-icon type="upload" />上传
        </a-button>
      </a-upload>
    </a-spin>
  </div>
</template>

api

import request from '@/utils/request'

const api = {
    uplodBackName: '/biz/uplodBackName'
}

export function getUplodBackName (parameter) {
  return request({
    url: api.uplodBackName,
    method: 'post',
    // 传输文件流需要单独设置请求头
    headers: {
      'Content-Type': 'multipart/form-data'
    },
    data: parameter
  })
}

js

<script>
import { getUplodBackName } from '@/api/biz/manage'

export default {
  components: {},
  data () {
    return {
      imageUrl: '',
      isShowSpinning: false
    }
  },
  methods: {
    // 上传图片前的钩子函数
    beforeImageUpload (file, fileList) {
      return new Promise(async (resolve, reject) => {
        if (!file.type.includes('image')) {
          this.$message.warning('请上传图片')
          reject(new Error('请上传图片'))
          return
        }
        this.isShowSpinning = true
        const newFile = await this.compressImg(file)
        resolve(newFile)
      })
    },
    // 自定义的上传请求
    customImageRequest (info) {
      const { file } = info
      // blob方式预览图片
      this.imageUrl = window.URL.createObjectURL(file)
      // 组装数据
      const formData = new FormData()
      formData.append('files', file)
      // 发送请求
      getUplodBackName(formData).then(res => {
        this.$message.success('上传成功')
      }).catch(() => {
        this.$message.warning('上传失败')
      }).finally(() => {
        this.isShowSpinning = false
      })
    },
    // base64转码(压缩完成后的图片为base64编码,这个方法可以将base64编码转回file文件)
    dataURLtoFile (dataurl, filename) {
      var arr = dataurl.split(',')
      var mime = arr[0].match(/:(.*?);/)[1]
      var bstr = atob(arr[1])
      var n = bstr.length
      var u8arr = new Uint8Array(n)
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n)
      }
      return new File([u8arr], filename, { type: mime })
    },
    // 图片压缩函数
    compressImg (file) {
      const that = this
      var files
      var fileSize = parseFloat(parseInt(file['size']) / 1024 / 1024).toFixed(2)
      var read = new FileReader()
      read.readAsDataURL(file)
      return new Promise(function (resolve, reject) {
        read.onload = function (e) {
          var img = new Image()
          img.src = e.target.result
          img.onload = function () {
            // 默认按比例压缩
            var w = this.width
            var h = this.height
            // 生成canvas
            var canvas = document.createElement('canvas')
            var ctx = canvas.getContext('2d')
            var base64
            // 创建属性节点
            canvas.setAttribute('width', w)
            canvas.setAttribute('height', h)
            ctx.drawImage(this, 0, 0, w, h)
            if (fileSize < 1) {
              // 如果图片小于一兆 那么压缩0.5
              base64 = canvas.toDataURL(file['type'], 0.5)
            } else if (fileSize > 1 && fileSize < 2) {
              // 如果图片大于1M并且小于2M 那么压缩0.5
              base64 = canvas.toDataURL(file['type'], 0.5)
            } else {
              // 如果图片超过2m 那么压缩0.2
              base64 = canvas.toDataURL(file['type'], 0.2)
            }
            // 回调函数返回file的值(将base64编码转成file)
            files = that.dataURLtoFile(base64, file.name) // 如果后台接收类型为base64的话这一步可以省略
            resolve(files)
          }
        }
      })
    }
  }
}
</script>

效果预览

压缩效果

根据实测,5MB的图片可以压缩到200KB,画质上,肉眼观察基本不变。

如有错误,请多指教,谢谢!

posted @ 2020-12-18 18:39  ykCoder  阅读(3013)  评论(0编辑  收藏  举报