1. 程式人生 > >Vue2.0圖片上傳及圖片壓縮自定義H5圖片上傳元件

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