Vue2.0圖片上傳及圖片壓縮自定義H5圖片上傳元件
最近公司要求圖片上傳需要壓縮,以前直接使用元件不能滿足使用了,於是決定自定義個圖片上傳元件。
可以實現動態傳入url,設定壓縮率,接收回傳引數。 壓縮也質量還不錯。
先上效果圖
效果如下
壓縮質量還不錯,4.37M到550k 壓縮率更是達到了87% ,這省了不少流量和伺服器硬碟啊,哈哈
1.元件html
定義了圖片上傳增加按鈕,將原有的input標籤樣式進行了更改,圖片顯示也進行樣式更改,利用vue2.0的v-for可以對圖片進行動態增加和刪除。
<div class="flex-img" id="img-arr"> <div class="upload-img" v-for="(imgstr,index) in imgStrArr"><i class="smui-icon smui-icon-cancel" @click="deleImg(index)"></i><img style="width:65px;height:65px" :src="imgstr"/></div> <div class="upload-add-img" style="position:relative;"> <i class="smui-icon smui-icon-camera"> <div class="img-input"> <input type="file" id="img-upload-file" name="myfile" @change="UpladFile(url,quality)" class="img-file" accept="image/x-png, image/jpg, image/jpeg, image/gif"/> </div> </i> </div> </div>
2.元件css
主要input標籤樣式進行了更改,佈局使用了flex佈局。flex佈局非常適用於移動端佈局。
新增圖片按鈕使用了相對佈局包含絕對佈局,將input和自定義的圖示進行重合。
.img-upload{ .flex-img{ display: -webkit-box; display: -ms-flexbox; display: flex; } .upload-img{ margin-left: 10px; margin-top: 10px; background: #fff; border: 1px solid #ddd; border-radius: 3px; width: 72px; height: 72px; text-align: center; font-size: 32px; position: relative; line-height: 74px; .smui-icon-cancel{ position: absolute; top: -10px; left: 60px; font-size: 16px; color: #ccc; } img{ margin: 3px; } } .upload-add-img{ margin:5px 15px 5px 10px; background: #FFFFFF; border: 1px solid #DDDDDD; border-radius: 3px; width: 80px; height: 80px; text-align: center; font-size: 32px; color: #ccc; line-height: 62px; } .img-input{ width:70px; height:70px; position: absolute; margin: auto; top: 0; left: 0; bottom: 0; right: 0; //background: #03c } .img-file { width:70px; height:70px; opacity:0;/*設定此控制元件透明度為零,即完全透明*/ filter:alpha(opacity=0);/*設定此控制元件透明度為零,即完全透明針對IE*/ //font-size:100px; position:absolute;/*絕對定位,相對於 .input */ top:0; right:0; } }
3.核心js程式碼,實現圖片壓縮和圖片上傳
基本步驟如下:
1.input標籤選擇圖片
2.獲取到file
3.利用fileReader()讀取圖片資訊(大小,圖片內容)
4.根據大小判斷是否需要壓縮,大於1M進行壓縮,小於1M直接上傳
//進行壓縮具體講解
4.1fileReader.onload執行後會將圖片轉換為base64編碼
4.2建立image物件將base64編碼作為源匯入,這裡呼叫image的onload方法,在方法內部建立cavas畫布,從新繪製圖片(canvas.toDataURL('image/jpeg', quality);quality值越小,所繪製出的影象越模糊),繪製完成重新生成新的base64圖片。這裡的圖片就是壓縮過後的圖片。
4.3將以base64的圖片url資料轉換為Blob
5.建立XMLHttpRequest 物件 post提交Blob圖片
6.回撥接收返回值
export default {
// url上傳地址 quality 質量比例 預設 0.7 0-1
props:['url','quality'],
name: 'imgUpload',
data() {
return {
xhr:{},
ot:0,
oloaded:0,
imgStrArr:[],
reportRecordId:''
}
},
methods:{
deleImg(i){
this.imgStrArr.pop(i);
},
/*
三個引數
file:一個是檔案(型別是圖片格式),
w:一個是檔案壓縮的後寬度,寬度越小,位元組越小
objDiv:一個是容器或者回調函式
photoCompress()
*/
photoCompress(file,w,objDiv){
var _this=this;
var ready=new FileReader();
/*開始讀取指定的Blob物件或File物件中的內容. 當讀取操作完成時,readyState屬性的值會成為DONE,如果設定了onloadend事件處理程式,則呼叫之.同時,result屬性中將包含一個data: URL格式的字串以表示所讀取檔案的內容.*/
ready.readAsDataURL(file);
ready.onload=function(){
var re=this.result;
_this.canvasDataURL(re,w,objDiv)
}
},
//重新繪製圖片
canvasDataURL(path, obj, callback){
var img = new Image();
img.src = path;
img.onload = function(){
var that = this;
// 預設按比例壓縮
var w = that.width,
h = that.height,
scale = w / h;
w = obj.width || w;
h = obj.height || (w / scale);
var quality = 0.7; // 預設圖片質量為0.7
//生成canvas
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
// 建立屬性節點
var anw = document.createAttribute("width");
anw.nodeValue = w;
var anh = document.createAttribute("height");
anh.nodeValue = h;
canvas.setAttributeNode(anw);
canvas.setAttributeNode(anh);
ctx.drawImage(that, 0, 0, w, h);
// 影象質量
if(obj.quality && obj.quality <= 1 && obj.quality > 0){
quality = obj.quality;
}
// quality值越小,所繪製出的影象越模糊
var base64 = canvas.toDataURL('image/jpeg', quality);
// 回撥函式返回base64的值
callback(base64);
}
},
/**
* 將以base64的圖片url資料轉換為Blob
* @param urlData
* 用url方式表示的base64圖片資料
*/
convertBase64UrlToBlob(urlData){
var arr = urlData.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while(n--){
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], {type:mime});
},
//上傳檔案方法
UpladFile(url,quality) {
console.log(url)
console.log(quality)
if(this.imgStrArr.length>2){
return;
}
var fileObj = document.getElementById("img-upload-file").files[0]; // js 獲取檔案物件
console.log('file',fileObj)
//var url = "http://10.118.62.42:8090/mobile/json/quickReport/upload.ht"; // 接收上傳檔案的後臺地址
//var url=this.url;
var form = new FormData(); // FormData 物件
var _this=this;
if(fileObj.size/1024 > 1025) { //大於1M,進行壓縮上傳
this.$loading.show({
text: 0 + '%'
});
this.photoCompress(fileObj, {
quality: quality
}, function(base64Codes){
//console.log("壓縮後:" + base.length / 1024 + " " + base);
var bl = _this.convertBase64UrlToBlob(base64Codes);
//console.log("base64Codes",base64Codes);
_this.imgStrArr.push(base64Codes);
form.append("file", bl, "file_"+Date.parse(new Date())+".jpg"); // 檔案物件
_this.xhr = new XMLHttpRequest(); // XMLHttpRequest 物件
_this.xhr.open("post", url, true); //post方式,url為伺服器請求地址,true 該引數規定請求是否非同步處理。
_this.xhr.setRequestHeader('reportRecordId',_this.reportRecordId);
_this.xhr.upload.onprogress = _this.progressFunction;//【上傳進度呼叫方法實現】
_this.xhr.onload = _this.uploadComplete; //請求完成
_this.xhr.onerror = _this.uploadFailed; //請求失敗
_this.xhr.upload.onloadstart = function(){//上傳開始執行方法
_this.ot = new Date().getTime(); //設定上傳開始時間
_this.oloaded = 0;//設定上傳開始時,以上傳的檔案大小為0
};
_this.xhr.send(form); //開始上傳,傳送form資料
});
}else{ //小於等於1M 原圖上傳
var reader = new FileReader();
reader.readAsDataURL(fileObj);
reader.onload=function(){
_this.imgStrArr.push(this.result);//this.result是base64編碼
//console.log(this.result);
}
form.append("file", fileObj); // 檔案物件
_this.xhr = new XMLHttpRequest(); // XMLHttpRequest 物件
_this.xhr.open("post", url, true); //post方式,url為伺服器請求地址,true 該引數規定請求是否非同步處理。
_this.xhr.setRequestHeader('reportRecordId',_this.reportRecordId);
_this.xhr.upload.onprogress = _this.progressFunction;//【上傳進度呼叫方法實現】
_this.xhr.onload = _this.uploadComplete; //請求完成
_this.xhr.onerror = _this.uploadFailed; //請求失敗
_this.xhr.upload.onloadstart = function(){//上傳開始執行方法
_this.ot = new Date().getTime(); //設定上傳開始時間
_this.oloaded = 0;//設定上傳開始時,以上傳的檔案大小為0
};
_this.xhr.send(form); //開始上傳,傳送form資料
}
},
//上傳成功響應
uploadComplete(evt) {
//服務斷接收完檔案返回的結果
//console.log('回覆',evt.target.responseText);
var data = JSON.parse(evt.target.responseText);
//console.log('回覆',data);
if(data.reportRecordId) {
this.reportRecordId=data.reportRecordId;
//傳遞資料給父元件
this.$emit('uplodCallBack',data.reportRecordId)
this.$toast.show({
type:'text',
text: '檔案上傳成功',
time:1000
})
}else{
this.$toast.show({
type:'text',
text: '檔案上傳失敗',
time:1000
});
this.imgStrArr.pop();
}
},
//上傳失敗
uploadFailed(evt) {
this.$toast.show({
type:'text',
text: '檔案上傳失敗',
time:1000
});
this.imgStrArr.pop();
},
//取消上傳
cancleUploadFile(){
this.xhr.abort();
},
//上傳進度實現方法,上傳過程中會頻繁呼叫該方法
progressFunction(progressEvent) {
if (progressEvent.lengthComputable) {
var num = Math.round(progressEvent.loaded / progressEvent.total * 100);
console.log("num=" + num);
if(num < 100){
this.$loading.show({
text: num + '%'
})
}else{
this.$loading.hide();
}
}
}
}
}
上面三部分就是元件的部分
4.元件的使用
將元件引入並註冊,就可以在父元件或父頁面上使用了
例如 我這個demo中直接使用註冊和soImgUpoad標籤就可以了
:url 範問後臺的 url:quality壓縮的質量 0-1之間可選quality值越小,所繪製出的影象越模糊 也就壓縮率越高
@uploadCallback回撥函式,圖片上傳成功後會呼叫該函式(預設傳回後臺返回引數)
<div id="app">
<img-upload :url="'http://XXX'" @uploadCallback="imgUplodCallBack" :quality="0.3"></img-upload>
</div>
import soImgUpload from '@/components/imgUpload'
export default {
components: {
imgUpload
},
data() {
return {
}
},
methods:{
imgUplodCallBack(r){
console.log('r',r);
}
}
}
5.總結
元件編寫需要熟悉元件間的資料傳遞,這裡用到了
1.父元件傳遞資料給子元件 通過props
2.子元件呼叫父元件方法 通過 this.$emit();
圖片壓縮用到了FileRerader物件和image物件,使用canvas重化圖片,是用base64編碼,需要熟悉base64編碼和圖片之間的轉換。
圖片上傳用到了XMLHttpRequest()
這些都是至少需要了解的
參考部落格:https://www.cnblogs.com/007sx/p/7583202.html