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
,畫質上,肉眼觀察基本不變。
如有錯誤,請多指教,謝謝!