vue實現(xiàn)壓縮圖片預(yù)覽并上傳功能(promise封裝)
本文實例為大家分享了vue實現(xiàn)壓縮圖片預(yù)覽并上傳的具體代碼,供大家參考,具體內(nèi)容如下
主要用到filereader、canvas 以及 formdata 這三個h5的api
過程大致分為三步:
用戶使用input file上傳圖片的時候,用filereader讀取用戶上傳的圖片數(shù)據(jù)(base64格式)
把圖片數(shù)據(jù)傳入img對象,然后將img繪制到canvas上,再調(diào)用canvas.toDataURL對圖片進(jìn)行壓縮
獲取到壓縮后的base64格式圖片數(shù)據(jù),轉(zhuǎn)成二進(jìn)制塞入formdata,再通過XmlHttpRequest提交formdata。
模板:
<template> <div class="image-box"> <input type="file" accept="image/*" @change="imageHandle"> <img ref="upImg"/> </div> </template>
獲取圖片數(shù)據(jù)
methods: {
//監(jiān)聽input file的change事件
imageHandle(e) {
//**這個是必不可少的,在下面的reader.onload中this就不再指vm了**
let that = this;
let maxSize = 100 * 1024;
let files = e.srcElement.files;
if (!files.length) return; //文件長度大于0
if (!/^image\//.test(files[0].type)) return; //必須是圖片才處理
if (!window.FileReader) return; //支持FileReader
//創(chuàng)建filereader對象
let reader = new FileReader();
reader.readAsDataURL(files[0]); //將圖片轉(zhuǎn)成base64格式
reader.onload = function() {
let result = this.result;
let img = new Image();
img.src = result;
let formdata = new FormData();
if (this.result.length <= maxSize) {
that.$refs.upImg.src = result; //預(yù)覽圖片
img = null;
//上傳圖片
formdata.append("image", that._upload(result, files[0].name, files[0].type));
that.$store.dispatch("uploadImage", formdata)
.then(data => {
if (data === 1) {
that.$toast("上傳成功", "success");
} else if (data === -1) {
that.$toast("圖片為空", "error");
} else {
that.$toast("上傳失敗", "error");
}
})
.catch(error => that.$toast("上傳失敗", "error"));
} else {
img.onload = function() {
//壓縮圖片
let data = that._compress(img);
//圖片預(yù)覽
that.$refs.upImg.src = data;
//上傳圖片
formdata.append("image", that._upload(data, files[0].name, files[0].type));
that.$store.dispatch("uploadImage", formdata)
.then(data => {
if (data === 1) {
that.$toast("上傳成功", "success");
} else if (data === -1) {
that.$toast("圖片為空", "error");
} else {
that.$toast("上傳失敗", "error");
}
})
.catch(error => that.$toast("上傳失敗", "error"));
};
}
};
},
壓縮圖片
在IOS中,canvas繪制圖片是有兩個限制的:
首先是圖片的大小,如果圖片的大小超過兩百萬像素,圖片也是無法繪制到canvas上的,調(diào)用drawImage的時候不會報錯,但是你用toDataURL獲取圖片數(shù)據(jù)的時候獲取到的是空的圖片數(shù)據(jù)。
再者就是canvas的大小有限制,如果canvas的大小大于大概五百萬像素(即寬高乘積)的時候,不僅圖片畫不出來,其他什么東西也都是畫不出來的。
應(yīng)對第一種限制,處理辦法就是瓦片繪制了。瓦片繪制,也就是將圖片分割成多塊繪制到canvas上,我代碼里的做法是把圖片分割成100萬像素一塊的大小,再繪制到canvas上。
而應(yīng)對第二種限制,我的處理辦法是對圖片的寬高進(jìn)行適當(dāng)壓縮,我代碼里為了保險起見,設(shè)的上限是四百萬像素,如果圖片大于四百萬像素就壓縮到小于四百萬像素。四百萬像素的圖片應(yīng)該夠了,算起來寬高都有2000X2000了。
如此一來就解決了IOS上的兩種限制了。
除了上面所述的限制,還有兩個坑,一個就是canvas的toDataURL是只能壓縮jpg的,當(dāng)用戶上傳的圖片是png的話,就需要轉(zhuǎn)成jpg,也就是統(tǒng)一用canvas.toDataURL(‘image/jpeg', 0.1) , 類型統(tǒng)一設(shè)成jpeg,而壓縮比就自己控制了。
另一個就是如果是png轉(zhuǎn)jpg,繪制到canvas上的時候,canvas存在透明區(qū)域的話,當(dāng)轉(zhuǎn)成jpg的時候透明區(qū)域會變成黑色,因為canvas的透明像素默認(rèn)為rgba(0,0,0,0),所以轉(zhuǎn)成jpg就變成rgba(0,0,0,1)了,也就是透明背景會變成了黑色。解決辦法就是繪制之前在canvas上鋪一層白色的底色。
_compress(img) {
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
//瓦片
let tCanvas = document.createElement("canvas");
let tctx = tCanvas.getContext("2d");
let initSize = img.src.length;
let width = img.width;
let height = img.height;
//如果圖片大于四百萬像素,計算壓縮比并將大小壓至400萬以下
let ratio;
if ((ratio = (width * height) / 4000000) > 1) {
ratio = Math.sqrt(ratio);
widht /= ratio;
height /= ratio;
} else {
ratio = 1;
}
canvas.width = width;
canvas.height = height;
//鋪底色
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, canvas.width, canvas.height);
//如果圖片像素大于100萬則使用瓦片繪制
let count;
if ((count = (width * height) / 1000000) > 1) {
count = ~~(Math.sqrt(count) + 1); //計算要分成多少瓦片,~~在這里表示取整
//計算每塊瓦片的寬高
let nw = ~~(width / count);
let nh = ~~(height / 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, canvas.width, canvas.height);
}
//進(jìn)行壓縮
let ndata = canvas.toDataURL("image/jpeg", 0.3);
tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0;
return ndata;
},
上傳
完成圖片壓縮后,就可以塞進(jìn)formdata里進(jìn)行上傳了,先將base64數(shù)據(jù)轉(zhuǎn)成字符串,再實例化一個ArrayBuffer,然后將字符串以8位整型的格式傳入ArrayBuffer,再通過BlobBuilder或者Blob對象,將8位整型的ArrayBuffer轉(zhuǎn)成二進(jìn)制對象blob,再將blob轉(zhuǎn)為File對象
_upload(data, name, type) {
let text = window.atob(data.split(",")[1]);
let buffer = new ArrayBuffer(text.length);
let ubuffer = new Uint8Array(buffer);
let pecent = 0,
loop = null;
for (var i = 0; i < text.length; i++) {
ubuffer[i] = text.charCodeAt(i);
}
let Builder =
window.BlobBuilder ||
window.WebKitBlobBuilder ||
window.MozBlobBuilder ||
window.MSBlobBuilder;
let blob;
if (Builder) {
var builder = new Builder();
builder.append(buffer);
blob = builder.getBlob(type);
} else {
blob = new window.Blob([ubuffer], { type: type });
}
// blob 轉(zhuǎn)file
var fileOfBlob = new File([blob], name, { type: type });
return fileOfBlob;
}
}
將圖片壓縮上傳封裝到一個js文件里
const UploadImg = {
imageHandle(files, maxSize, imgDom) {
let that = this;
let formdata = new FormData();
let reader = new FileReader();
reader.readAsDataURL(files[0]); //將圖片轉(zhuǎn)成base64格式
//reader.onload是異步,要用到Promise對象將值返回出去
return new Promise((resolved, rejected) => {
reader.onload = function () {
let result = this.result;
let img = new Image();
img.src = result;
if (this.result.length <= maxSize) {
imgDom.src = result;
img = null;
formdata.append("image", that._upload(result, files[0].name, files[0].type));
resolved(formdata);
} else {
img.onload = function () {
let data = that._compress(img);
imgDom.src = data;
formdata.append("image", that._upload(data, files[0].name, files[0].type));
resolved(formdata);
};
}
};
})
},
_compress(img) {
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
//瓦片
let tCanvas = document.createElement("canvas");
let tctx = tCanvas.getContext("2d");
let width = img.width;
let height = img.height;
//如果圖片大于四百萬像素,計算壓縮比并將大小壓至400萬以下
let ratio;
if ((ratio = (width * height) / 4000000) > 1) {
ratio = Math.sqrt(ratio);
widht /= ratio;
height /= ratio;
} else {
ratio = 1;
}
canvas.width = width;
canvas.height = height;
//鋪底色
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, canvas.width, canvas.height);
//如果圖片像素大于100萬則使用瓦片繪制
let count;
if ((count = (width * height) / 1000000) > 1) {
count = ~~(Math.sqrt(count) + 1); //計算要分成多少瓦片
//計算每塊瓦片的寬高
let nw = ~~(width / count);
let nh = ~~(height / 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, canvas.width, canvas.height);
}
//進(jìn)行最小壓縮
let ndata = canvas.toDataURL("image/jpeg", 0.3);
tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0;
return ndata;
},
_upload(data, name, type) {
let text = window.atob(data.split(",")[1]);
let buffer = new ArrayBuffer(text.length);
let ubuffer = new Uint8Array(buffer);
for (var i = 0; i < text.length; i++) {
ubuffer[i] = text.charCodeAt(i);
}
let Builder =
window.BlobBuilder ||
window.WebKitBlobBuilder ||
window.MozBlobBuilder ||
window.MSBlobBuilder;
let blob;
if (Builder) {
var builder = new Builder();
builder.append(buffer);
blob = builder.getBlob(type);
} else {
blob = new window.Blob([ubuffer], { type: type });
}
// blob 轉(zhuǎn)file
var fileOfBlob = new File([blob], name, { type: type });
return fileOfBlob;
}
}
export default UploadImg
調(diào)用代碼
import UploadImg from "../../util/uploadImg";
methods: {
imageHandle(e) {
let maxSize = 100 * 1024;
let imgDom = this.$refs.upImg;
let files = e.srcElement.files;
if (!files.length) return; //文件長度大于0
if (!/^image\//.test(files[0].type)) return; //必須是圖片才處理
if (!window.FileReader) return; //支持FileReader
if (this.docEntry === "" || this.lineId === "") {
this.$toast("請?zhí)顚懲暾畔?, "error");
return;
}
// let formdata = new FormData();
UploadImg.imageHandle(files, maxSize, imgDom).then(formdata => {
formdata.append("docEntry", this.docEntry);
formdata.append("lineId", this.lineId);
formdata.append("action", "ProductionListImage");
this.$store
.dispatch("uploadImage", formdata)
.then(data => {
if (data === 1) {
this.$toast("上傳成功", "success");
} else if (data === -1) {
this.$toast("圖片為空", "error");
} else {
this.$toast("上傳失敗", "error");
}
})
.catch(error => this.$toast("上傳失敗", "error"));
});
}
}
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Vue計算屬性與監(jiān)視(偵聽)屬性的使用深度學(xué)習(xí)
這篇文章主要介紹了Vue計算屬性與監(jiān)視(偵聽)屬性的使用,計算屬性指的是通過一系列運(yùn)算之后,最終得到一個值,watch監(jiān)視(偵聽)器允許開發(fā)者監(jiān)視數(shù)據(jù)的變化,從而針對數(shù)據(jù)的變化做特定的操作,本文就這兩種屬性給大家詳細(xì)講解,感興趣的朋友一起學(xué)習(xí)吧2022-11-11
vue2.0 實現(xiàn)導(dǎo)航守衛(wèi)的具體用法(路由守衛(wèi))
這篇文章主要介紹了vue2.0 實現(xiàn)導(dǎo)航守衛(wèi)的具體用法(路由守衛(wèi)),vue-route 提供的 beforeRouteUpdate 可以方便地實現(xiàn)導(dǎo)航守衛(wèi)(navigation-guards),具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-05-05
vue第三方庫中存在擴(kuò)展運(yùn)算符報錯問題的解決方案
這篇文章主要介紹了vue第三方庫中存在擴(kuò)展運(yùn)算符報錯問題,本文給大家分享解決方案,通過結(jié)合實例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07
關(guān)于Vue 自定義指令實現(xiàn)元素拖動的詳細(xì)代碼
這篇文章主要介紹了Vue 自定義指令實現(xiàn)元素拖動,在使用自定義指令之前,先對自定義指令有一定的了解,主要從自定義指令定義范圍,鉤子函數(shù)方面入手,需要的朋友可以參考下2022-01-01

