前端在項(xiàng)目開(kāi)發(fā)中缚态,會(huì)經(jīng)常遇到需要這樣的需求:
1.將用戶(hù)上傳的資料通過(guò)接口傳遞給后端
2.對(duì)于圖片類(lèi)型的資料,需要在前端進(jìn)行壓縮至200kb左右
難點(diǎn)1:
在開(kāi)發(fā)中,尤其是axios,默認(rèn)的傳參格式是josn摹察,而上傳文件的接口一般傳參格式都是form-data,在使用接口時(shí)倡鲸,參數(shù)在瀏覽器中的表現(xiàn)形式是這樣的:
難點(diǎn)2:
如何進(jìn)行圖片的壓縮
解決思路:
在封裝接口參數(shù)的時(shí)候供嚎,通過(guò)new FormData()創(chuàng)建實(shí)例,并通過(guò)append方法向?qū)嵗刑砑訉傩郧妥矗⑶沂謩?dòng)添加屬性cantentType
form_data.append('contentType', 'multipart/form-data')
axios攔截器請(qǐng)求攔截的時(shí)候加入對(duì)于contentType的識(shí)別克滴,并在contentType等于multipart/form-data的時(shí)候?qū)鲄⒏袷礁臑閙ultipart/form-data
// 請(qǐng)求攔截
service.interceptors.request.use((req) => {
if (req.data.contentType === 'multipart/form-data') {
req.headers['Content-Type'] = req.data.contentType
}
}
對(duì)于需要傳遞給后端的圖片數(shù)據(jù),在前端我們進(jìn)行一個(gè)壓縮的處理优床,我將之封裝為了一個(gè)方法compressImg劝赔,代碼如下:
// 生成圖片對(duì)象
const readImg = (file) => {
return new Promise((resolve, reject) => {
const img = new Image()
const reader = new FileReader()
reader.onload = function (e) {
// @ts-ignore
img.src = e.target.result
}
reader.onerror = function (e) {
reject(e)
}
reader.readAsDataURL(file)
img.onload = function () {
resolve(img)
}
img.onerror = function (e) {
reject(e)
}
})
}
// 利用canvas壓縮圖片
export const compressImg = async (fileData, mx = 1000, mh = 1000) => {
const canvasImgData = await readImg(fileData)
return new Promise((resolve, reject) => {
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')
// @ts-ignore
const { width: originWidth, height: originHeight } = canvasImgData
let dataURL = ''
// 最大尺寸限制
const maxWidth = mx
const maxHeight = mh
// 目標(biāo)尺寸
let targetWidth = originWidth
let targetHeight = originHeight
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > 1) {
// 寬圖片
targetWidth = maxWidth
targetHeight = Math.round(maxWidth * (originHeight / originWidth))
} else {
// 高圖片
targetHeight = maxHeight
targetWidth = Math.round(maxHeight * (originWidth / originHeight))
}
}
canvas.width = targetWidth
canvas.height = targetHeight
context.clearRect(0, 0, targetWidth, targetHeight)
// 圖片繪制
// @ts-ignore
context.drawImage(canvasImgData, 0, 0, targetWidth, targetHeight)
dataURL = canvas.toDataURL('image/jpeg') // 轉(zhuǎn)換圖片為dataURL
resolve(dataURL)
// 轉(zhuǎn)換為bolb對(duì)象
// canvas.toBlob(function(blob) {
// resolve(blob)
// }, type || 'image/png')
})
}
用戶(hù)點(diǎn)擊頁(yè)面選擇圖片進(jìn)行本地上傳操作后,我們可以拿到對(duì)應(yīng)的數(shù)據(jù)胆敞,將這個(gè)數(shù)據(jù)作為參數(shù)傳遞給compressImg函數(shù)着帽,如果用的是vant的van-uploader組件杂伟,取:after-read="afterUploadRead"
的第一個(gè)默認(rèn)參數(shù)file的file屬性
const afterUploadRead = (file, details) => {
console.log(file.file)
}
如果是element的el-upload組件,則取得是:before-upload="beforeUpload"
事件的默認(rèn)參數(shù)file
const beforeUpload = (file) => {
console.log(file)
}
不管是何種情況仍翰,最終傳遞給compressImg方法的參數(shù)應(yīng)該是這樣的數(shù)據(jù)
上傳頁(yè)面壓縮圖片數(shù)據(jù)赫粥,封裝接口參數(shù)并觸發(fā)接口的代碼如下:
const finalCompressData = await compressImg(file.file)
let form_data = new FormData()
let fileName =
file.file.name ||
Math.floor(Math.random() * 100000) +
'.' +
file.content.split(';')[0].split('/')[1].replace('jpeg', 'jpg')
form_data.append('file', dataURLtoBlob(finalCompressData), fileName)
form_data.append('contentType', 'multipart/form-data')
uploadfile(form_data).then((res: any) => {
}
這樣壓縮后的圖片數(shù)據(jù),基本都只有100多kb予借,同時(shí)清晰度也是可以正常查看的越平。
注意,上面的這個(gè)compressImg函數(shù)只能壓縮圖片數(shù)據(jù)灵迫,其他格式的如視頻等秦叛,都是沒(méi)法壓縮的。