1. 程式人生 > >H5使用Base64和Canvas上傳圖片

H5使用Base64和Canvas上傳圖片

    最近在做一個班級通知的H5頁面,從拿到原型到開發出完整功能的過程中,解決了一些問題,現在記錄一下發布通知時遇到的難點和解決方法。

一、呼叫手機攝像頭和手機相簿選取照片

1、由於使用的是最接近原生Android的mui框架,所以最開始是準備用mui的plus來呼叫camera和gallery方法,但是後來發現一直出現plusReady未定義的錯誤,寫在plusReady中的程式碼沒有執行。    在DCloud中找到原因如下,普通瀏覽器裡沒有plus環境,只有HBuilder真機執行和打包後才能執行plus api,且個人嘗試在真機中也不能執行。公司使用mui在初始階段,本人更是小白,到了這裡最後還是決定使用JS來實現照片的選擇和上傳。2、使用input標籤
呼叫攝像頭和相簿,accpet表示開啟系統檔案目錄,capture表示可捕獲到的系統裝置,multiple表示多選。 <input type="file" accept="image/*" >// 開啟相機或相簿② <input type="file" accept="image/*" capture="camera">// 開啟相機,可以拍照③ <input type="file" accept="video/*" capture="camcorder">// 開啟錄影機,可以錄影④ <input type="file" accept="audio/*" capture="microphone">// 開啟錄音機,可以錄音     選擇input標籤後,覺得和已有的UI風格相差很大,介面顯示如下:                                             

     可以設定 opacity:0 將已有的input格式隱藏,然後將文字資訊居中顯示。
<input style="opacity:0;width:1px" type="file" id="file_input" accept="image/*" multiple>選擇圖片</input>

二、Base64。

1、簡介。     Base64是一種用64個字元【A-Z  a-z  0-9  + /】來表示任意二進位制資料的方法,常用於在URL、Cookie、數字證書籤名或網頁中傳輸少量二進位制資料。     將每3個位元組Byte即3*8=24bit,轉化為4組6個二進位制位,然後在6位的前面補兩個0,形成一個8位的位元組,若剩下的字元不足3個位元組,則用0填充,輸出字元使用'=',因此編碼後輸出的文字末尾可能會出現1或2個'='【解碼時會自動去掉
】。 通過查表獲得4個編碼後的字元,由於Base64把3位元組的二進位制資料編碼為4位元組的文字資料,所以長度增加33%左右。                 
2、為什麼使用Base64。① 減少HTTP請求數,提高頁面載入速度。對於只有幾個KB大小的圖片,進行HTTP請求附帶的額外資訊可能比圖片本身還大,當請求量大時,資料傳輸會變慢【以data:image/*;base64開頭】。② 將圖片使用Base64編碼後,可通過URL直接解碼檢視圖片,即內嵌到網頁中而不是去請求並載入進來。③ 可以防止因為一些相對路徑等問題導致的圖片404錯誤。3、Base64的缺點。① Base64是一種通過查表的編碼方法,不能用於加密,即使使用自定義的編碼表也不行。② 當需要傳輸的圖片在1M以上時【未確定臨界值】,在網頁中載入Base64會變得很慢,此時需對圖片進行壓縮處理。

三、使用Base64和Canvas儲存圖片的編碼。

1、使用FileReader將檔案轉化為Base64編碼。① 在H5中使用FileReader把檔案讀取到記憶體,並讀取檔案中的資料,以便對圖片進行預覽。② FileReader的readAsDataURL方法可以將讀取的檔案資料以DataURL的形式即Base64儲存,可在客戶端直接顯示。2、使用Canvas對圖片重新繪製並設定壓縮質量。① 將圖片轉化成Base64後,可以繪製到Canvas上並對圖片進行編輯操作。② 當編輯或壓縮完成後,可以使用Canvas的toDataURL方法輸出新圖片的Base64資料。HTML:
<div class="row mui-input-row ">
			<textarea id="container" name="txt" class="mui-input-clear question"
				placeholder="請輸入正文(必填,最多輸入500個漢字)"></textarea>
			<div class="add_picture_div">
				<a href="#picture" class="add_picture"><img src="${base}/res/images/add.png"></a>
			</div>

			<div id="picture" class="mui-popover mui-popover-action mui-popover-bottom">
				<ul class="mui-table-view">
					<li class="mui-table-view-cell">
						<input style="opacity:0;width:1px" type="file" id="file_input" accept="image/*" multiple>選擇圖片</input>
					</li>
					<li class="mui-table-view-cell"><a href="#"><b>取消</b></a></li>
				</ul>
			</div>
		</div>
JavaScript:
var myHtml="";
		window.onload = function(){  
		    var result,input = document.getElementById("file_input");  
		    if(typeof FileReader==='undefined'){
		        result.innerHTML = "抱歉,你的瀏覽器不支援 FileReader";  
		        input.setAttribute('disabled','disabled');  
		    }else{  
		        input.addEventListener('change',readFile,false);  
		    } 
		    function readFile(){  
	            var iLen = this.files.length;  
		        for(var i=0;i<iLen;i++){  
		            if (!input['value'].match(/.jpg|.gif|.png|.bmp/i)){  //判斷上傳檔案格式  
		                return alert("上傳的圖片格式不正確,請重新選擇");  
		            }  
		            var reader = new FileReader();  
		            var fileName = this.files[i].name; 	  // 獲取檔名
		            var fileType = this.files[i].type;	  // 檔案字尾
		            reader.readAsDataURL(this.files[i]);  // 轉成base64,此方法執行完後,base64資料儲存在reader.result裡 
		            reader.onload = function(e){
		            	var image = new Image();		  // 建立一個image物件,供canvas繪圖使用	
		            	image.src = this.result;		  // this.result即base64的資料
		            	image.onload = function(){
		            		var scale = 1;
		            		var cvs = document.createElement('canvas');
		            		if(this.width > 300 || this.height > 300){
		            			if(this.width > this.height)
		            				scale = 300 / this.width;
		            			else scale = 300 / this.height;
		            		}
		            		cvs.width = scale * this.width;		// 計算等比縮小後圖片寬
		            		cvs.height = scale * this.height;
		            		var ctx = cvs.getContext('2d');		// 返回一個用於在畫布上二維繪圖的環境
		            		ctx.drawImage(this,0,0,cvs.width,cvs.height);
		            		var newImage = cvs.toDataURL(fileType,0.7);	// 重新生成圖片Base64,壓縮質量即壓縮率為0.7
		            		myHtml += "<br><p style='text-align:center;'><img src='"+newImage+"'/></p>";
			                var aHtml = '<a href="#" class="add_picture"><img src="'+newImage+'"/></a>';
			                $(".add_picture_div").append(aHtml);
		            	}
		            }
		        }  
		    }  
		}
jQuery:
$.ajax({
				url:'${base}/classedit/notice/noticePublish',
				type:"post",
				data:{
					ownerId:$("#clsId").val(),		// 班級ID
					listTitle:$("#title").val(),
					txt:$("#container").val()+myHtml
				},
				success:function(result){
					console.info(result);
					if(result.status == true){
						alert("釋出成功!");
						window.location.href='toNoticeListPage?userId=${userId!'' }';
					}else{
						alert("釋出失敗!");
						window.location.reload();
					}
				}
			});
讓釋出的通知以原樣顯示到詳情頁中,可以使用<pre>標籤格式化 freemarker 取出的資料。pre 元素可定義預格式化的文字,被包圍在 pre 元素中的文字通常會保留空格和換行符,儲存到資料庫中的資料會以原格式顯示到頁面中。
<div class="mui-media-body">
			<h4>${cmsContentExt.title }</h4>
			<h6 class="mt15">${cmsContentExt.author } ${cmsContentExt.releaseDate?string("yyyy-MM-dd HH:mm:ss") }</h6>
			<h6 class="mui-ellipsis mt20 fong_gray">
				<#if listCD ??>
					<#list listCD as list>
						${list.className }
					</#list>
				</#if>
			</h6>
			<span class="mt20 fong_gray"><pre>${cmsContentTxt.txt}</pre></span>
		</div>
3、效果圖:
① 釋出通知:                                             
② 按Scale等比縮放圖片畫素                                                               
③ 使用img{max-width:100%;} 設定,若圖片大於屏寬則100%顯示,圖片小於屏寬則顯示原圖。                                                      
     至此,H5頁面釋出通知功能開發完成。