鑷姩涓婁紶
image-conversion NPM 鍦板潃
瀹夎鎻掍欢
npm i image-conversion --save
鍒╃敤before-upload
閽╁瓙鍑芥暟锛屽湪涓婁紶涔嬪墠鐢?code>image-conversion鎻掍欢鐨?compressAccurately 鏂规硶瀵瑰浘鐗囪繘琛屽帇缂╁鐞嗐€?/p>
<!--鍗曞浘涓婁紶缁勪欢/鎸夐挳-->
<template>
<el-upload
:action="uploadUrl"
name="avatar"
:multiple="false"
:show-file-list="false"
:before-upload="beforeUpload"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
with-credentials
>
</el-upload>
</template>
<script>
import { compress, compressAccurately } from 'image-conversion'
export default {
setup(props, { emit }) {
// ...鐪佺暐鍏朵粬
const beforeUpload = file => {
const typeList = ['image/jpeg', 'image/png', 'image/gif']
const isTypeValid = typeList.includes(file.type)
const isLt2M = file.size / 1024 / 1024 < 2
if (!isTypeValid) {
ElMessage.error('鍥剧墖鏍煎紡鍙兘鏄?JPG/PNG/GIF!')
}
if (!isLt2M) {
ElMessage.error('鍥剧墖澶у皬涓嶈兘瓒呰繃 2MB!')
}
return new Promise((resolve, reject) => {
if (file.size / 1024 > 200) { // 澶т簬 200 kb 灏卞帇缂?
compressAccurately(file, 200).then(res => {
// The res in the promise is a compressed Blob type (which can be treated as a File type) file;
console.log(res)
resolve(res)
})
} else if (isTypeValid && isLt2M) {
// 鏃犻渶鍘嬬缉鐩存帴杩斿洖鍘熸枃浠?
resolve(file)
} else {
reject()
}
})
}
return {
beforeUpload
}
}
}
</script>
鎵嬪姩涓婁紶
鎵嬪姩涓婁紶闇€灏?el-upload 缁勪欢鐨?code>auto-upload灞炴€ц缃负 false锛岃繖鏍?code>before-upload閽╁瓙渚垮け鏁堜簡銆傝繖鏃跺彲浠ョ敤on-change
閽╁瓙鍑芥暟浠f浛锛?/p>
鍥犱负鏈夋壒閲忎笂浼犵殑鎯呭喌锛屾敞鎰忚繖涓挬瀛愭槸浼犱簡鍑犱釜鏂囦欢灏辫Е鍙戝嚑娆$殑锛屾垜浠篃鍙互鍦ㄦ彁浜よ〃鍗曟椂鍐嶅幓瀵瑰浘鐗囨枃浠惰繘琛屽帇缂╁鐞嗐€?/p>
鍚屾椂 image-conversion 鏄紓姝ヨ繑鍥炵粨鏋滐紝鏃犳硶鎺ユ敹锛屾垜浠渶瑕佹崲涓€涓柟寮忥紝鍒╃敤 canvas 鐢诲竷瀵瑰浘鐗囪繘琛屽帇缂╋細
鍏舵牳蹇冨師鐞嗗氨鏄妸涓€寮犲ぇ鐨勫浘鐗囷紝鐩存帴鐢诲湪涓€寮犲皬灏忕殑鐢诲竷涓娿€傛鏃跺ぇ鍥剧墖灏卞彉鎴愪簡灏忓浘鐗囷紝鍘嬬缉灏辫繖涔堝疄鐜颁簡銆?/p>
canvas
鐨?code>drawImage API锛欳anvasRenderingContext2D.drawImage()
ctx.drawImage(image, dx, dy);
ctx.drawImage(image, dx, dy, dWidth, dHeight);
ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
image
锛氬嵆闇€缁樺埗鍒颁笂涓嬫枃鐨勫浘鐗囧厓绱犮€傚厑璁镐换浣曠殑 canvas 鍥惧儚婧?CanvasImageSource
)锛屽彲浠ユ槸椤甸潰涓婅幏鍙栫殑 DOM 瀵硅薄锛屼篃鍙互鏄?strong>铏氭嫙 DOM 涓殑鍥剧墖瀵硅薄 (鎴戜滑鐨勪笂浼犳搷浣滃氨鏄櫄鎷?DOM 瀵硅薄锛屽苟娌℃湁鐪熸缁樺埗鍒伴〉闈笂)銆?br>
dx, dy, dWidth, dHeight
锛?br>
琛ㄧず鍦?canvas 鐢诲竷涓婅鍒掑涓€鐗囧尯鍩熺敤鏉ユ斁缃浘鐗囷紝dx, dy
鍒嗗埆涓虹洰鏍?canvas
鍏冪礌鐨勫乏涓婅X 銆乊杞村潗鏍囧潗鏍囷紝dWidth, dHeight
琛ㄧずcanvas
鍏冪礌涓婄敤浜庢樉绀哄浘鐗囩殑鍖哄煙澶у皬锛屽厑璁稿缁樺埗鐨?image 杩涜缂╂斁銆?br>
濡傛灉娌℃湁鎸囧畾sx,sy,sWidth,sHeight
杩?涓弬鏁帮紝鍒欏浘鐗囦細琚媺浼告垨缂╂斁鍦ㄨ繖鐗囧尯鍩熷唴銆?/p>
瀹為檯鎿嶄綔寰堢畝鍗曪紝姣斿闇€瑕佹妸涓€寮犲浘鐗囩殑灏哄缂╂斁涓?code>400*200澶у皬锛?/p>
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d');
canvas.width = 400;
canvas.height = 200;
// 鏍稿績 js
ctx.drawImage(img, 0, 0, 400, 200)
閭e浣曟妸 canvas 鐢诲竷杞崲鎴?img 鍥惧儚鍛紝canvas鎻愪緵浜?涓浆鍥剧墖鐨勬柟娉曪細
-
canvas.toDataURL(type, quality)
API 鍦板潃锛欻TMLCanvasElement.toDataURL()
鎶婂浘鐗囪浆鎹㈡垚 base64 鏍煎紡淇℃伅锛岀函瀛楃鐨勫浘鐗囪〃绀烘硶銆?/li>
-
type
锛?鍥剧墖鏍煎紡锛岄粯璁mage/png -
quality
锛氬湪鎸囧畾鍥剧墖鏍煎紡涓?image/jpeg 鎴?image/webp鐨勬儏鍐典笅锛屽彲浠ュ湪 0 鍒?1 鐨勫尯闂撮€夋嫨鍥剧墖鐨勮川閲忋€傚鏋滆秴鍑哄彇鍊艰寖鍥达紝灏嗕細浣跨敤榛樿鍊?0.92銆?/li>
-
toBlob(callback, type, quality)
锛?br> API 鍦板潃锛欻TMLCanvasElement.toBlob()
鎶?canvas 杞崲鎴?Blob鏂囦欢锛岄€氬父鐢ㄥ湪鏂囦欢涓婁紶涓紝鍥犱负鏄簩杩涘埗鐨勶紝瀵瑰悗绔洿鍔犲弸濂姐€?/li>
-
callback
锛氬洖璋冨嚱鏁帮紝杞崲濂界殑Blob
瀵硅薄銆傚鏋滃浘鍍忔湭琚垚鍔熷垱寤猴紝鍙兘浼氳幏寰?null
鍊笺€?/li> -
type
鍜?code>quality锛氬拰toDataURL()
鐨勫弬鏁颁竴鑷?/li>
鍜?code>toDataURL()鏂规硶鐩告瘮锛?code>toBlob()鏂规硶鏄紓姝ョ殑锛屽洜姝ゅ浜嗕釜callback
鍙傛暟銆傚紓姝ユ剰鍛崇潃涓嶅彲鎺ф€э紝閭d箞濡傛灉鎴戜滑闇€瑕佺殑涓婁紶鏍煎紡鏄?code>Blob锛屽彲浠ラ€氳繃鍏堣浆鎴?code>dataURL锛屽啀鎶?code>dataURL杞垚Blob
鐨勫鐞嗘柟寮忋€?/p>
鐜板湪鏉ョ湅鐪嬪叿浣撲唬鐮佹€庝箞瀹炵幇鈥滃浘鐗団啋canvas鍘嬬缉鈫掑浘鐗団€?/strong>杩欎釜杩囩▼锛?/h5>
绠€鍗曟暣鐞嗕笅鎬濊矾锛?/p>
- 棣栧厛鎷垮埌鍥剧墖璧勬簮锛岀敤 FileReader 鍘昏鍙栵紝璇荤殑杩囩▼涓敤
readAsDataURL
鏂规硶鎶?File 杞垚dataURL
锛坆ase64缂栫爜瀛楃涓诧級
- 璇诲彇鎴愬姛瑙﹀彂 reader 鐨?load 浜嬩欢锛屾鏃舵妸
dataURL
璧嬪€肩粰鍒涘缓鐨勭┖new Image()
鐨?code>src
- image 寮€濮嬪姞杞斤紝瑙﹀彂 image 鐨?onload 浜嬩欢锛屽湪姝ゅ杩涜鍘嬬缉锛屽啀灏嗗帇缂╁畬鐨勫€艰繑鍥炶繃鏉ャ€?/li>
- 鍏蜂綋鍘嬬缉杩囩▼鏄敤 canvas 鍦ㄨ櫄鎷?DOM 涓粡杩囩畻娉曟寜姣斾緥缁樺埗鍥剧墖锛岀劧鍚?br>
灏嗙粯鍒跺畬鐨?canvas 杞垚 dataURL锛屽啀鎶?dataURL 杞垚 Blob
- 鎺ョ潃鎶? Blob append 鍒?FormData 瀹炰緥瀵硅薄涓婏紙濡傛湁闇€瑕侊紝鍙互鍦ㄦ嬁鍒板埌鍘嬬缉瀹岀殑Blob 缁撴灉鍚庯紝鍐嶅皢鍏惰浆鎹负 File锛?/li>
涓嬮潰鎶婂叿浣撲唬鐮佽创涓€涓嬶紙鐪佺暐鏃犲叧閮ㄥ垎锛夛細
馃憞
灏佽鐨勫帇缂╂柟娉昷s锛?/p>
// `src/utils/compress.js`
export function dataURLToBlob(dataUrl) {
let arr = dataUrl.split(',')
let mime = arr[0].match(/:(.*?);/)[1]
let bytes = window.atob(arr[1]) // 鍘绘帀url鐨勫ご锛屽苟杞崲涓篵yte
var n = bytes.length
var u8arr = new Uint8Array(n)
while (n--) { // 灏嗗浘鍍忔暟鎹€愬瓧鑺傛斁鍏int8Array涓?
u8arr[n] = bytes.charCodeAt(n)
}
return new Blob([u8arr], { type: mime })
}
export const compress = (img, option, Orientation) => {
const options = option || {}
const { width, height, quality } = options
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
let initSize = img.src.length
let w = width || img.width
let h = height || img.height
// 濡傛灉鍥剧墖澶т簬鍥涚櫨涓囧儚绱狅紝璁$畻鍘嬬缉姣斿苟灏嗗ぇ灏忓帇鑷?00涓囦互涓?
let ratio
if ((ratio = (w * h) / 4000000) > 1) {
ratio = Math.sqrt(ratio)
w /= ratio
h /= ratio
} else {
ratio = 1
}
canvas.width = w
canvas.height = h
ctx.fillStyle = '#fff'
ctx.fillRect(0, 0, w, h)
// 鐡︾墖canvas
const tCanvas = document.createElement('canvas')
const tctx = tCanvas.getContext('2d')
let count
if ((count = (w * h) / 1000000) > 1) {
// 濡傛灉鍥剧墖鍍忕礌澶т簬100涓囧垯浣跨敤鐡︾墖缁樺埗
console.log('瓒呰繃100W鍍忕礌')
count = ~~(Math.sqrt(count) + 1) //璁$畻瑕佸垎鎴愬灏戝潡鐡︾墖
// 璁$畻姣忓潡鐡︾墖鐨勫鍜岄珮
let nw = ~~(w / count)
let nh = ~~(h / count)
tCanvas.width = nw
tCanvas.height = nh
for (let i = 0; i < count; i++) {
for (let j = 0; j < count; j++) {
tctx.drawImage(
img,
i * nw * ratio,
j * nh * ratio,
nw * ratio,
nh * ratio,
0,
0,
nw,
nh
)
ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh)
}
}
} else {
ctx.drawImage(img, 0, 0, w, h)
}
//淇 ios涓婁紶鍥剧墖鐨勬椂鍊?琚棆杞殑闂
if (Orientation != '' && Orientation != 1) {
switch (Orientation) {
case 6: //闇€瑕侀『鏃堕拡锛堝悜宸︼級90搴︽棆杞?
rotateImg(img, 'left', canvas)
break
case 8: //闇€瑕侀€嗘椂閽堬紙鍚戝彸锛?0搴︽棆杞?
rotateImg(img, 'right', canvas)
break
case 3: //闇€瑕?80搴︽棆杞?
rotateImg(img, 'right', canvas) //杞袱娆?
rotateImg(img, 'right', canvas)
}
}
// 杩涜鏈€灏忓帇缂?
// canvas.toDataURL(type, encoderOptions);
// type锛?鍥剧墖鏍煎紡 image/png
// encoderOptions锛氬湪鎸囧畾鍥剧墖鏍煎紡涓?image/jpeg 鎴?image/webp鐨勬儏鍐典笅锛屽彲浠ュ湪 0 鍒?1 鐨勫尯闂撮€夋嫨鍥剧墖鐨勮川閲忋€?
// 濡傛灉瓒呭嚭鍙栧€艰寖鍥达紝灏嗕細浣跨敤榛樿鍊?0.92銆?
let newImg = canvas.toDataURL('image/jpeg', quality || 0.3)
console.log('鍘嬬缉鍓嶏細' + initSize)
console.log('鍘嬬缉鍚庯細' + newImg.length)
console.log('ndata:' + newImg)
console.log(
'鍘嬬缉鐜囷細' + ~~((100 * (initSize - newImg.length)) / initSize) + '%'
)
// return new File([blob], file.name, { type: file.mime }) // 濡傛灉鏄繑鍥?File
// 榛樿杩斿洖 Blob 鏍煎紡锛屽吋瀹规€ц緝濂?
// newImg = base64UrlToBlob(newImg)
newImg = dataURLToBlob(newImg)
tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0
return newImg
}
/** 鍥剧墖鍘嬬缉锛岄粯璁や负鍚屾瘮渚嬪帇缂?
* @param {file} 鎺ュ彈鏂囦欢鏁扮粍鎴栧崟涓枃浠?鍥剧墖瀵硅薄)
* @param {option} 鎸囧畾鍘嬬缉鐨勯厤缃弬鏁帮紝 width锝?height锝?quality
* 鍥炶皟鍑芥暟 callback 鍙傛暟涓?base64 瀛楃涓插浘鐗囨暟鎹暟缁?
*/
export function compressFile(file, option, callback) {
const reader = new FileReader()
const image = new Image()
// 寮傛璇诲彇浼犲叆鐨?Blob 鎴?File 瀵硅薄锛屻€愯鍙栨垚鍔熴€戜細瑙﹀彂 reader 鐨?load 浜嬩欢
reader.readAsDataURL(file)
// 璇诲彇鍒板浘鐗囨暟鎹繚瀛樺湪 event.target 鐨?result灞炴€т腑
reader.onload = function (event) {
// 鏁版嵁鏍煎紡鏄?data:URL瀛楃涓诧紙base64缂栫爜锛?
image.src = event.target.result
}
return new Promise((resolve, reject) => {
image.onload = () => {
resolve(compress(image, option))
}
})
}
鎵归噺涓婁紶缁勪欢锛?/p>
<!--鎵归噺涓婁紶缁勪欢-->
<template>
<el-upload
:action="`${apiUrl}/admin/upload`"
name="photoSrc"
with-credentials
:show-file-list="true"
:file-list="photoList"
:on-change="photoListChange"
:on-preview="handlePreview"
:on-remove="handleRemove"
:auto-upload="false"
:drag="drag"
multiple
:class="uploadClass"
>
<template v-if="!drag" #trigger>
<el-button size="small" type="primary">閫夊彇鏂囦欢</el-button>
</template>
<template v-if="drag">
<i class="el-icon-upload" />
<div class="el-upload__text">
灏嗗浘鐗囨嫋鍒版澶勶紝<br />鎴?lt;em>鐐瑰嚮涓婁紶</em>
</div>
</template>
</el-upload>
</template>
<script lang="ts">
import { compressFile } from '@/utils/compress'
export default defineComponent({
name: 'UploadMulti',
setup(props) {
const photoList = ref([])
// 褰撴枃浠跺垪琛ㄦ湁鏀瑰彉鏃惰Е鍙戯紝娣诲姞鏂囦欢銆佷笂浼犳垚鍔熷拰涓婁紶澶辫触鏃堕兘浼氳璋冪敤銆?
// 鎴戜滑閰嶇疆浜嗘墜鍔ㄤ笂浼狅紝鍥犳鍙湁娣诲姞鍥剧墖鏃惰Е鍙?
const photoListChange = (file: Object) => {
const typeList = ['image/jpeg', 'image/png', 'image/gif']
const isTypeValid = typeList.includes(file.raw.type)
const isLt2M = file.size / 1024 / 1024 < 2
if (!isTypeValid) return ElMessage.error('鍥剧墖鏍煎紡鍙兘鏄?JPG/PNG/GIF!')
if (!isLt2M) return ElMessage.error('鍥剧墖澶у皬涓嶈兘瓒呰繃 2MB!')
// 鍘嬬缉澶勭悊
compressFile(file.raw).then(res => {
const newFile = new File([res], file.name, { type: file.raw.type })
file.blobUrl = res
file.raw = Object.assign({}, newFile)
photoList.value.push(file)
})
// photoList.value.push(file)
}
// 浼犲叆 true 鑾峰彇銆愬凡涓婁紶銆戞枃浠讹紝鍚﹀垯鏄幏鍙栥€愭湭涓婁紶銆戞枃浠?
// 鐢ㄦ槸鍚︽嫢鏈?raw 灞炴€?鏉ュ垽鏂紝鏈?raw property 灏辨槸寰呬笂浼犵殑
const filterPhotoList = (isUploaded?: Boolean) => {
return isUploaded
photoList.value.filter(
item => !Object.prototype.hasOwnProperty.call(item, 'raw')
)
: photoList.value.filter(item =>
Object.prototype.hasOwnProperty.call(item, 'raw')
)
}
// 琛ㄥ崟鎻愪氦鏃跺厛瑙﹀彂杩欎釜鏂规硶鐢ㄦ潵鑾峰彇鏂囦欢鍒楄〃锛屽鏈夋湭涓婁紶鐨勫氨鍦ㄨ繖鏃朵笂浼?
const getUploadedList = async () => {
// 鑾峰彇寰呬笂浼犵殑鏂囦欢鍒楄〃锛屽鏋滄病鏈夊氨鐩存帴杩斿洖 photoList 鐨勫€?
const toUploadList = filterPhotoList()
if (toUploadList.length === 0) return photoList.value
// 鏈夊垯寮€濮嬫墜鍔ㄤ笂浼?
const formData = new FormData()
// 鍘嬬缉澶勭悊涔熷彲浠ヨ€冭檻鏀捐繖閲?
// for (let file of toUploadList) {
// const res = await compressFile(file)
// ...
// formData.append('photoSrc', file.blobUrl, file.name)
// }
toUploadList.forEach(file => {
// 濡傛灉鏄?Blob
formData.append('photoSrc', file.blobUrl, file.name)
// formData.append('photoSrc', file.raw, file.name)
})
// 鎵ц涓婁紶锛寀ploadMulti 鏄垜鐨勬壒閲忎笂浼犳帴鍙?
const { data } = await uploadMulti(formData)
// 瀵硅繑鍥炵殑鍥剧墖淇℃伅鍋氫簺澶勭悊锛屽啀缁欒〃鍗曞幓缁х画 Post
return filterPhotoList(true).concat(uploadMultiSuccess(data))
}
return {
photoList,
photoListChange,
filterPhotoList,
getUploadedList,
}
}
})
</script>
鍙傝€冿細
鍓嶇JS鍒╃敤canvas鐨刣rawImage()瀵瑰浘鐗囪繘琛屽帇缂?br>
vue涓嬪疄鐜癷nput瀹炵幇鍥剧墖涓婁紶锛屽帇缂╋紝鎷兼帴浠ュ強鏃嬭浆
readAsDataURL
鏂规硶鎶?File 杞垚dataURL
锛坆ase64缂栫爜瀛楃涓诧級dataURL
璧嬪€肩粰鍒涘缓鐨勭┖new Image()
鐨?code>src
馃憞
灏佽鐨勫帇缂╂柟娉昷s锛?/p>
// `src/utils/compress.js`
export function dataURLToBlob(dataUrl) {
let arr = dataUrl.split(',')
let mime = arr[0].match(/:(.*?);/)[1]
let bytes = window.atob(arr[1]) // 鍘绘帀url鐨勫ご锛屽苟杞崲涓篵yte
var n = bytes.length
var u8arr = new Uint8Array(n)
while (n--) { // 灏嗗浘鍍忔暟鎹€愬瓧鑺傛斁鍏int8Array涓?
u8arr[n] = bytes.charCodeAt(n)
}
return new Blob([u8arr], { type: mime })
}
export const compress = (img, option, Orientation) => {
const options = option || {}
const { width, height, quality } = options
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
let initSize = img.src.length
let w = width || img.width
let h = height || img.height
// 濡傛灉鍥剧墖澶т簬鍥涚櫨涓囧儚绱狅紝璁$畻鍘嬬缉姣斿苟灏嗗ぇ灏忓帇鑷?00涓囦互涓?
let ratio
if ((ratio = (w * h) / 4000000) > 1) {
ratio = Math.sqrt(ratio)
w /= ratio
h /= ratio
} else {
ratio = 1
}
canvas.width = w
canvas.height = h
ctx.fillStyle = '#fff'
ctx.fillRect(0, 0, w, h)
// 鐡︾墖canvas
const tCanvas = document.createElement('canvas')
const tctx = tCanvas.getContext('2d')
let count
if ((count = (w * h) / 1000000) > 1) {
// 濡傛灉鍥剧墖鍍忕礌澶т簬100涓囧垯浣跨敤鐡︾墖缁樺埗
console.log('瓒呰繃100W鍍忕礌')
count = ~~(Math.sqrt(count) + 1) //璁$畻瑕佸垎鎴愬灏戝潡鐡︾墖
// 璁$畻姣忓潡鐡︾墖鐨勫鍜岄珮
let nw = ~~(w / count)
let nh = ~~(h / count)
tCanvas.width = nw
tCanvas.height = nh
for (let i = 0; i < count; i++) {
for (let j = 0; j < count; j++) {
tctx.drawImage(
img,
i * nw * ratio,
j * nh * ratio,
nw * ratio,
nh * ratio,
0,
0,
nw,
nh
)
ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh)
}
}
} else {
ctx.drawImage(img, 0, 0, w, h)
}
//淇 ios涓婁紶鍥剧墖鐨勬椂鍊?琚棆杞殑闂
if (Orientation != '' && Orientation != 1) {
switch (Orientation) {
case 6: //闇€瑕侀『鏃堕拡锛堝悜宸︼級90搴︽棆杞?
rotateImg(img, 'left', canvas)
break
case 8: //闇€瑕侀€嗘椂閽堬紙鍚戝彸锛?0搴︽棆杞?
rotateImg(img, 'right', canvas)
break
case 3: //闇€瑕?80搴︽棆杞?
rotateImg(img, 'right', canvas) //杞袱娆?
rotateImg(img, 'right', canvas)
}
}
// 杩涜鏈€灏忓帇缂?
// canvas.toDataURL(type, encoderOptions);
// type锛?鍥剧墖鏍煎紡 image/png
// encoderOptions锛氬湪鎸囧畾鍥剧墖鏍煎紡涓?image/jpeg 鎴?image/webp鐨勬儏鍐典笅锛屽彲浠ュ湪 0 鍒?1 鐨勫尯闂撮€夋嫨鍥剧墖鐨勮川閲忋€?
// 濡傛灉瓒呭嚭鍙栧€艰寖鍥达紝灏嗕細浣跨敤榛樿鍊?0.92銆?
let newImg = canvas.toDataURL('image/jpeg', quality || 0.3)
console.log('鍘嬬缉鍓嶏細' + initSize)
console.log('鍘嬬缉鍚庯細' + newImg.length)
console.log('ndata:' + newImg)
console.log(
'鍘嬬缉鐜囷細' + ~~((100 * (initSize - newImg.length)) / initSize) + '%'
)
// return new File([blob], file.name, { type: file.mime }) // 濡傛灉鏄繑鍥?File
// 榛樿杩斿洖 Blob 鏍煎紡锛屽吋瀹规€ц緝濂?
// newImg = base64UrlToBlob(newImg)
newImg = dataURLToBlob(newImg)
tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0
return newImg
}
/** 鍥剧墖鍘嬬缉锛岄粯璁や负鍚屾瘮渚嬪帇缂?
* @param {file} 鎺ュ彈鏂囦欢鏁扮粍鎴栧崟涓枃浠?鍥剧墖瀵硅薄)
* @param {option} 鎸囧畾鍘嬬缉鐨勯厤缃弬鏁帮紝 width锝?height锝?quality
* 鍥炶皟鍑芥暟 callback 鍙傛暟涓?base64 瀛楃涓插浘鐗囨暟鎹暟缁?
*/
export function compressFile(file, option, callback) {
const reader = new FileReader()
const image = new Image()
// 寮傛璇诲彇浼犲叆鐨?Blob 鎴?File 瀵硅薄锛屻€愯鍙栨垚鍔熴€戜細瑙﹀彂 reader 鐨?load 浜嬩欢
reader.readAsDataURL(file)
// 璇诲彇鍒板浘鐗囨暟鎹繚瀛樺湪 event.target 鐨?result灞炴€т腑
reader.onload = function (event) {
// 鏁版嵁鏍煎紡鏄?data:URL瀛楃涓诧紙base64缂栫爜锛?
image.src = event.target.result
}
return new Promise((resolve, reject) => {
image.onload = () => {
resolve(compress(image, option))
}
})
}
<!--鎵归噺涓婁紶缁勪欢-->
<template>
<el-upload
:action="`${apiUrl}/admin/upload`"
name="photoSrc"
with-credentials
:show-file-list="true"
:file-list="photoList"
:on-change="photoListChange"
:on-preview="handlePreview"
:on-remove="handleRemove"
:auto-upload="false"
:drag="drag"
multiple
:class="uploadClass"
>
<template v-if="!drag" #trigger>
<el-button size="small" type="primary">閫夊彇鏂囦欢</el-button>
</template>
<template v-if="drag">
<i class="el-icon-upload" />
<div class="el-upload__text">
灏嗗浘鐗囨嫋鍒版澶勶紝<br />鎴?lt;em>鐐瑰嚮涓婁紶</em>
</div>
</template>
</el-upload>
</template>
<script lang="ts">
import { compressFile } from '@/utils/compress'
export default defineComponent({
name: 'UploadMulti',
setup(props) {
const photoList = ref([])
// 褰撴枃浠跺垪琛ㄦ湁鏀瑰彉鏃惰Е鍙戯紝娣诲姞鏂囦欢銆佷笂浼犳垚鍔熷拰涓婁紶澶辫触鏃堕兘浼氳璋冪敤銆?
// 鎴戜滑閰嶇疆浜嗘墜鍔ㄤ笂浼狅紝鍥犳鍙湁娣诲姞鍥剧墖鏃惰Е鍙?
const photoListChange = (file: Object) => {
const typeList = ['image/jpeg', 'image/png', 'image/gif']
const isTypeValid = typeList.includes(file.raw.type)
const isLt2M = file.size / 1024 / 1024 < 2
if (!isTypeValid) return ElMessage.error('鍥剧墖鏍煎紡鍙兘鏄?JPG/PNG/GIF!')
if (!isLt2M) return ElMessage.error('鍥剧墖澶у皬涓嶈兘瓒呰繃 2MB!')
// 鍘嬬缉澶勭悊
compressFile(file.raw).then(res => {
const newFile = new File([res], file.name, { type: file.raw.type })
file.blobUrl = res
file.raw = Object.assign({}, newFile)
photoList.value.push(file)
})
// photoList.value.push(file)
}
// 浼犲叆 true 鑾峰彇銆愬凡涓婁紶銆戞枃浠讹紝鍚﹀垯鏄幏鍙栥€愭湭涓婁紶銆戞枃浠?
// 鐢ㄦ槸鍚︽嫢鏈?raw 灞炴€?鏉ュ垽鏂紝鏈?raw property 灏辨槸寰呬笂浼犵殑
const filterPhotoList = (isUploaded?: Boolean) => {
return isUploaded
photoList.value.filter(
item => !Object.prototype.hasOwnProperty.call(item, 'raw')
)
: photoList.value.filter(item =>
Object.prototype.hasOwnProperty.call(item, 'raw')
)
}
// 琛ㄥ崟鎻愪氦鏃跺厛瑙﹀彂杩欎釜鏂规硶鐢ㄦ潵鑾峰彇鏂囦欢鍒楄〃锛屽鏈夋湭涓婁紶鐨勫氨鍦ㄨ繖鏃朵笂浼?
const getUploadedList = async () => {
// 鑾峰彇寰呬笂浼犵殑鏂囦欢鍒楄〃锛屽鏋滄病鏈夊氨鐩存帴杩斿洖 photoList 鐨勫€?
const toUploadList = filterPhotoList()
if (toUploadList.length === 0) return photoList.value
// 鏈夊垯寮€濮嬫墜鍔ㄤ笂浼?
const formData = new FormData()
// 鍘嬬缉澶勭悊涔熷彲浠ヨ€冭檻鏀捐繖閲?
// for (let file of toUploadList) {
// const res = await compressFile(file)
// ...
// formData.append('photoSrc', file.blobUrl, file.name)
// }
toUploadList.forEach(file => {
// 濡傛灉鏄?Blob
formData.append('photoSrc', file.blobUrl, file.name)
// formData.append('photoSrc', file.raw, file.name)
})
// 鎵ц涓婁紶锛寀ploadMulti 鏄垜鐨勬壒閲忎笂浼犳帴鍙?
const { data } = await uploadMulti(formData)
// 瀵硅繑鍥炵殑鍥剧墖淇℃伅鍋氫簺澶勭悊锛屽啀缁欒〃鍗曞幓缁х画 Post
return filterPhotoList(true).concat(uploadMultiSuccess(data))
}
return {
photoList,
photoListChange,
filterPhotoList,
getUploadedList,
}
}
})
</script>
鍓嶇JS鍒╃敤canvas鐨刣rawImage()瀵瑰浘鐗囪繘琛屽帇缂?br> vue涓嬪疄鐜癷nput瀹炵幇鍥剧墖涓婁紶锛屽帇缂╋紝鎷兼帴浠ュ強鏃嬭浆