在XMLHttpRequest Level2出臺以前,大多數的異步上傳圖片都是利用iframe去實現的。php
至於具體的實現細節,我就不在這邊囉嗦的,Google一下就有文章談這個東西。html
此次主要說說,怎麼用新的API去實現圖片上傳。前端
首先,少不了的天然是XMLHttpRequest Level2的一些新特性啦。web
其中最爲實在的就是FormData對象,直接把表單(form)的DOM對象轉爲FormData對象,而後向服務器發送。api
還有就是Progress事件的支持,異步上傳終於能夠查看進度條啦!數組
這裏我就不廢話了,由於大多數人應該都看過的 阮一峯 的 《XMLHttpRequest Level 2 使用指南》,直接貼代碼吧。瀏覽器
1 var formData= new FormData(form), 2 xhr= new XMLHttpRequest(); 3 4 xhr.open("POST", url); 5 xhr.send(formData);
接口的部分也很簡單,例如PHP,直接用$_POST、$_FILES就能夠拿到相關的數據.服務器
沒錯,就是這麼簡單。app
而經過監聽Progress時間,就能夠判斷當前數據上傳/下載的進度。異步
1 xhr.upload.onprogress = function (e) { 2 console.log(e.loaded / e.total * 100); // 上傳進度 3 } 4 5 xhr.onprogress = function (e) { 6 console.log(e.loaded / e.total * 100); // 下載進度 7 }
至於XMLHttpRequest Level2的支持狀況,在移動端仍是比較理想的。
一直以來,無數的前端屌絲都渴望瀏覽器能夠提供JavaScript訪問本地文件的API。
其實大老早,IE就能利用ActiveX來操做本地文件了,但由於並不是W3C的標準,一直就只有IE在玩。
於25 October 2012,W3C訂立了File API的草案。
另外,和File對象配套使用的,還有FileReader對象。具體有什麼用後面再說。
下面是它們倆的支持度,狀況比較通常。不過先學着就差很少了,哈~
先說說File對象吧。
File對象是來自用戶在一個<input>元素上選擇文件後返回的FileList對象,也能夠是來自由拖放操做生成的 DataTransfer對象.
以下:
1 // input:file的File對象 2 document.querySelector("input[type=file]").files; // return FileList 3 4 // drop事件的File對象 5 elem.ondrop = function (e) { 6 e.preventDefault(); 7 return e.dataTransfer.files; // return FileList 8 };
都是以一個FileList的對象返回。這個FileList的對象相似於NodeList。有length屬性,但並不是數組。
下面是Chrome Console打印出來的FileList對象:
再看看File對象有什麼屬性:
那些屬性都表明什麼意思我就再也不廢話了。但咱們能夠留意到,File對象是繼承於Blob對象,至於什麼是Blob對象,後面的篇幅我們再說起。
一開始,咱們還提到一個FileReader的對象,說是配套着File對象一塊兒用。
使用FileReader對象,web應用程序能夠異步的讀取存儲在用戶計算機上的文件(或者原始數據緩衝)內容,能夠使用File對象或者Blob對象來指定所要讀取的文件或數據.
具體怎麼用,咱們看看下面的代碼:
1 // 建立一個FileReader對象 2 var reader = new FileReader(); 3 4 // 綁定load事件 5 reader.onload = function(e) { 6 console.log(e.target.result); 7 } 8 9 // 讀取File對象的數據 10 reader.readAsDataURL(document.querySelector("input[type=file]").files[0]);
當FileReader對象經過readAsDataURL讀取數據成功後,就會觸發load事件。target中的result屬性的值,就是該文件的base64數據
固然,FileReader對象不僅僅只有readAsDataURL一個方法。
1 /** 2 * 停止該讀取操做.在返回時,readyState屬性的值爲DONE. 3 */ 4 reader.abort(); 5 6 7 /** 8 * 開始讀取指定的Blob對象或File對象中的內容. 9 * 當讀取操做完成時,readyState屬性的值會成爲DONE,若是設置了onloadend事件處理程序,則調用之. 10 * 同時,result屬性中將包含一個data: URL格式的字符串以表示所讀取文件的內容. 11 */ 12 reader.readAsDataURL(file); 13 14 15 /** 16 * 同上, result屬性中將包含一個字符串以表示所讀取的文件內容. 17 * encoding是可選項,類型爲字符串,表示了返回數據所使用的編碼.若是不指定,默認爲UTF-8. 18 */ 19 reader.readAsText(file[, encoding ]); 20 21 22 /** 23 * 同上, result屬性中將包含一個ArrayBuffer對象以表示所讀取文件的內容. 24 */ 25 reader.readAsArrayBuffer(file); 26 27 28 /** 29 * 同上, result屬性中將包含所讀取文件的原始二進制數據. 30 */ 31 reader.readAsBinaryString(file);
既然拿到了文件的base64,那作事情就方便多了。
例如,咱們能夠直接把base64的字符串post到服務器端。
1 var reader = new FileReader(); 2 3 reader.onload = function(e) { 4 var xhr = new XMLHttpRequest(), 5 fromData = new FormData(); 6 7 fromData.append("base64", e.target.result); 8 xhr.open("post",url, true); 9 xhr.send(fromData); 10 } 11 12 reader.readAsDataURL(document.querySelector("input[type=file]").files[0]);
是否是以爲有什麼不對?
既然都用FormData了,還轉個毛線base64啊!
轉成base64以後,最大的好處就是能夠繪製到Canvas上,而後對圖片進行編輯!
在客戶端這邊作裁剪啊塗鴉啊什麼的,編輯完成後再利用Canvas對象的toDataURL方法,就能夠輸出編輯後圖片的base64數據。
細節的實現我這裏就不說了。
咱們再看看HTML5的file表單元素提供了什麼新的支持:
1 <input type="file" accept="image/*" id="file_image" name="file_image" multiple /> 2 <input type="file" accept="video/*" id="file_video" name="file_video" />
accept屬性,能夠用來限制用戶上傳文件的類型。這個屬性IOS和OSX支持得很好。
另外還有multiple屬性,意思就是能夠選擇多個文件。添加了這個屬性以後,再配合FormData對象,能夠實現批量上傳。
1 (function (W, D) { 2 var fileForm = document.getElementById("file_form"), 3 fileImage = document.getElementById("file_image"); 4 5 function testAjax(files, i) { 6 if (i < 0) { 7 return ; 8 } 9 10 var xhr = new XMLHttpRequest(), 11 data = new FormData(); 12 13 xhr.onload = function () { 14 // 遞歸 15 testAjax(files, --i); 16 }; 17 data.append("file_image", files[i]); 18 xhr.open("post", "test/demo2.php", true); 19 xhr.send(data); 20 } 21 22 fileForm.addEventListener("submit", function (e) { 23 e.preventDefault(); 24 var files = fileImage.files; 25 26 testAjax(files, --files.length); 27 }, false); 28 29 })(window, document);
好了,寫到這裏就差很少了。
額...
是否是忘記什麼了?
哦!Blob對象!
它嘛,就是一個二進制數據的對象。這個玩意就有空再說了,想了解的童鞋本身去看文檔吧。
最後,吐槽一下。
經測試,MIUI V4 & MIUI V5 的自帶瀏覽器(原生?),FileReader對象中base64數據的結構出錯,缺乏了圖像類型的數據。
正確的base64格式:data:image/jpeg;base64,iVBO…AL5wF1K4MqU0AAAAASUVORK5CYII=
MIUI自帶瀏覽器的格式:data:base64,iVBO…AL5wF1K4MqU0AAAAASUVORK5CYII=
參考資料:
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input
https://developer.mozilla.org/zh-CN/docs/DOM/File
https://developer.mozilla.org/zh-CN/docs/DOM/XMLHttpRequest/FormData
https://developer.mozilla.org/es/docs/XMLHttpRequest/Usar_XMLHttpRequest
http://caniuse.com/#feat=fileapi
http://caniuse.com/#feat=filereader
本文做者:Maple Jan