HTML5上傳圖片及其相關知識點

前幾日做了項目,其中有一個模塊涉及到上傳圖片至服務器。今天抽空整理了下,發現越整理涉及的知識點越多,下面例子有參考百度的搜圖。

知識點:input file、base64、FileReader、canvas壓縮、blob、btoa編碼和atob解碼、FormData。

html dom節點:

<input type="file">默認可以選擇一個文件。需要上傳多張。可以增加multiple="true" 屬性。一般使用opacity:0;將默認樣式隱藏,然後再重新寫其樣式。


FileReader : https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader(參考文檔)

1、創建對象

var fileReader = new FileReader();

2、判斷瀏覽器是否兼容----ie8下不支持

if( window.FileReader )

3、狀態常量

常量名 描述
EMPTY 0 爲開始讀取文件
LOADING 1 文件讀取中
DONE 2 文件讀取完成

在下面例子中,可以分別讀取當前狀態。

4、屬性

屬性名 描述
error 讀取文件時發生錯誤
readyState 當前fileReader對象的狀態,爲上述狀態常量的一個
result 讀取到的內容

5、方法

方法名 參數 描述
abort 中止讀取,在非LOADING狀態時調用會拋出異常
readAsArrayBuffer blob/file 讀取爲數組,在result中有一個ArrayBuffer對象爲讀取的內容
readAsBinaryString blob/file 讀取爲二進制,在result中有讀取文件的原始二進制
readAsDataUrl blob/file 讀取爲dataUrl,在result中有data:url格式的字符串表示讀取的內容
readAsTexx blob/file , [encoding] 讀取爲文本,在result中字符串表示讀取的內容

6、事件處理

事件 描述
onabort 中斷時觸發
onerror 出錯時觸發
onload 讀取成功時觸發
onloadend 讀取完成時觸發(不論成功是否)
onloadstart 讀取開始時觸發
onprocess 讀取中觸發

BASE64:

我們用chrome打開一張圖片,在resources裏面顯示的就是圖片的base編碼(實際上base編碼比原圖片稍大)

圖片的base64編碼也就是將一張圖片編碼成一個字符串,我們可以用這個字符串給img標籤的src賦值,這樣我們就可以看到這張圖片。


如何編寫:

在html中:

<img src="data:image/gif;base64,R0lGODlhAwADAIABAL6+vv///yH5BAEAAAEALAAAAAADAAMAAAIDjA9WADs=">
在css中:

background-image:url(data:image/gif;base64,R0lGODlhBAABAIABAMLBwfLx8SH5BAEAAAEALAAAAAAEAAEAAAICRF4AOw==);

優缺點:

優點:1、減少了http請求;2、可以被gzip;3、沒有跨域問題;4、無需考慮在更新圖片時緩存問題。

缺點:1、ie8以下不支持;2、不論是寫在css文件還是html文件中,增加了文件的大小;3、圖片大了之後,程序員編碼相當困難;


應用:

根據實際需求來選擇base64顯示圖片,或者選擇css sprite,或者直接使用png等

一般使用場景:很少被更新,實際尺寸很小,在系統中大量使用。


canvas壓縮:

在移動應用場景中,用戶上傳的圖片一般很大,會導致上傳時間過長而失敗,既浪費時間也浪費流量,更影響用戶體驗。我們可以使用canvas的drawImage方法的圖形裁剪功能。

1、新建image對象,給其src複製base64值,在其監聽onload事件;

2、在onload事件方法中新建canvas對象,獲取上下文context;

3、設置裁剪比例,調用drawImage方法填充圖片。

4、通過toDataUrl方法獲取裁剪之後的base64值。

詳細見下例。


Blob

在傳輸一些比較大的圖片的base64是容易出現轉發錯誤,這裏我們可以將base64轉換成blob字段寫到form表單中提交到後臺。一般blob和base64之間的相互轉換通過fileReader 的readAsDataUrl和ArrayBuffer的charCodeAt方法。下面列舉幾個相互轉換的方法。來自(http://jsperf.com/blob-base64-conversion)

 var blobToBase64 = function(blob, cb) {
      var reader = new FileReader();
      reader.onload = function() {
        var dataUrl = reader.result;
        var base64 = dataUrl.split(',')[1];
        cb(base64);
      };
      reader.readAsDataURL(blob);
      };

  var base64ToBlob = function(base64, cb) {
      var binary = atob(base64);
      var len = binary.length;
      var buffer = new ArrayBuffer(len);
      var view = new Uint8Array(buffer);
      for (var i = 0; i < len; i++) {
        view[i] = binary.charCodeAt(i);
      }
      cb(new Blob([view]));
      };

  var base64ToBlobSync = function(base64) {
      var binary = atob(base64);
      var len = binary.length;
      var buffer = new ArrayBuffer(len);
      var view = new Uint8Array(buffer);
      for (var i = 0; i < len; i++) {
        view[i] = binary.charCodeAt(i);
      }
      var blob = new Blob([view]);
      return blob;
      };

  var blobToBase64_2 = function(blob, cb) {
      var reader = new FileReader();
      reader.onload = function() {
        var buffer = reader.result;
        var view = new Uint8Array(buffer);
        var binary = String.fromCharCode.apply(window, view);
        var base64 = btoa(binary);
        cb(base64);
      };
      reader.readAsArrayBuffer(blob);
      };


btoa 與 atob:      ---在對base64轉blob時就需要用atob對base64進行解碼

btoa("javascript");     //"amF2YXNjcmlwdA=="

atob("amF2YXNjcmlwdA==") ;       //"javascript"

注意:在需要轉碼中文時,需要用encodeURIComponent方法對中文處理,解碼時用decodeURIComponent

btoa(encodeURIComponent("我喜歡 javascript"));    //"JUU2JTg4JTkxJUU1JTk2JTlDJUU2JUFDJUEyJTIwamF2YXNjcmlwdA=="

decodeURIComponent(atob("JUU2JTg4JTkxJUU1JTk2JTlDJUU2JUFDJUEyJTIwamF2YXNjcmlwdA=="));   //"我喜歡 javascript"


FormData:

我們只需要使用new FormData()創建對象,然後append鍵值對,後用ajax向後臺發生即可。

這裏是往FormData對象添加blob字段。

注:使用Ajax將這個FormData對象提交到服務器上時,所發送的HTTP請求頭中代表那個Blob對象所包含文件的文件名稱的"Content-Disposition"請求頭的值會是一個空字符串,這會引發某些服務器程序上的錯誤.從Gecko 7.0開始,這種情況下發送的文件名稱改爲"blob"這個字符串.



本次整理實例:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
	<title>Document</title>
	<script src="http://apps.bdimg.com/libs/zepto/1.1.4/zepto.min.js"></script>   <!--引用baidu-->
</head>
<style>
.uploadPic{
	width: 92%;
  	position: relative;
  	margin: 0 auto;
  	height: 2.8rem;
  	line-height: 2.8rem;
  	font-size: 1.3rem;
  	border-radius: 4px;
  	color: #fff;
  	text-align: center;
  	background-color: #72bcc5;
}
.uploadPic>input{
	position: absolute;
  	display: block;
  	width: 100%;
  	height: 100%;
  	right: 0;
  	top: 0;
  	opacity: 0;
}
.uploadPic>img{
	border:none;
}
</style>
<body>
	<div id="uploadPic" class="uploadPic">
		<span>拍攝</span>
		<input type="file" >  <!--這裏可以添加multiple="true"屬性,用來添加多張圖片,然後對this.file[]數組操作-->
		<p style="line-height: 1rem;margin: 0.5rem 0;"><progress id="progress" value="0" max="100" style="width: 100%;"></progress></p> 
		<img src="" width="100%" style="height: 21rem;">
	</div>
</body>
<script>
	function upload(file, callBack) {
        var loading=0;
        var total=file.size;
        if(window.FileReader){
        	$("#progress")[0].value=0;
	        var fileReader = new FileReader();
	        fileReader.onload = function() {
	        	console.log(fileReader.readyState);  //讀取完成
	            compressPic(this.result,callBack)
	        };
	        fileReader.onerror = function() {
	        	console.log(fileReader.error);
	        };
	        fileReader.onprogress = function (e){
	        	console.log(fileReader.readyState);  //讀取中
	        	loading += e.loaded;
	        	$("#progress")[0].value=(loading / total) * 100; 
	        }
	        console.log(fileReader.readyState);  //未讀取
	        fileReader.readAsDataURL(file)
        }else{
        	alert("您的瀏覽器不支持FileReader");
        }
    }

    function base64ConvertToBlob(picData, type, size) {
            type = type || "";
            size = size || 512;
            var decodeFileData = atob(picData);  //此處用atob解碼,轉碼函數btoa。在使用方法時注意操作中文時,需對中文decodeURIComponent轉換
            var dataArray = [];
            var len = decodeFileData.length;
            for (var i = 0; i < len; i += size) {
                var pieceData = decodeFileData.slice(i, i + size);   //這裏做了一個512的分組
                var arr = new Array(pieceData.length);
                for (var j = 0; j < pieceData.length; j++) {
                    arr[j] = pieceData.charCodeAt(j)
                }
                var u8a = new Uint8Array(arr);
                dataArray.push(u8a)
            }
            return new Blob(dataArray, {type: type})
    }

    function compressPic(picData,callBack) {
        var img=new Image();
        img.onload = function(){
	       	var width = img.width;
	       	var height = img.height;
	       	var standard=800;  //以800爲基準壓縮
	       	if (width > standard || height > standard) {
	           	var rate = Math.max(width / standard, height / standard);
	           	width /= rate;
	           	height /= rate
	       	}
	    	var canvas = document.createElement("canvas");
	       	canvas.width = width;
	       	canvas.height = height;
	       	var context = canvas.getContext("2d");
	       	context.fillRect(0, 0, canvas.width, canvas.height);
	       	context.drawImage(img, 0, 0, width, height);
	       	var data = canvas.toDataURL("image/jpeg", 1);
	       	//var blobData=base64ConvertToBlob(data.replace(/^.*?,/, ""), "image/jpeg")  //-----需要去掉符號,不然使用atob方法報錯
	       	//doAjax(new FormData().append('image',data));   //後續可以這樣做,轉換成Blob字段,組裝FormData,發送至後臺
	       	console.log("the after canvas compress size : " + data.length);
	       	callBack(data)
        };
        img.src=picData;
        console.log("the before canvas conpress size : "+picData.length);
    }

	$("#uploadPic input").change(function() {
		var file=this.files[0];
		upload(file, function(picData){
            	$("#uploadPic img")[0].src = picData;    //預覽
            }
        );
    });
</script>
</html>



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章