在Web FileReader API接口實現以前,圖片預覽的一般作法是先將圖片上傳至服務器,上傳成功之後經過過觸發ajax請求到剛剛上傳的圖片,而後加載到頁面。這個過程當中若是圖片選擇錯誤或者須要修改上傳的圖片,就須要重複上傳和下載請求,而且還須要在服務器替換圖片資源,會浪費大量的網絡資源和服務器資源。如今經過FileReader實現本地圖片讀取預覽,就能夠在本地實現圖片修改,節省服務器資源。css
既然是HTML5的API就目前來講確定存在兼容性問題,目前IE10開始支持FileReader,因此經過服務上傳下載的圖片預覽方式仍是有必要的,接下來的示例僅僅展現FileReader的圖片讀取預覽代碼:html
1 <style> 2 .imgBox{ 3 display: flex; 4 width: 300px; 5 height: 300px; 6 border: 1px solid #300; 7 justify-content: center; 8 align-items: center; 9 } 10 </style> 11 <input type="file" name=""> 12 <div class="imgBox"></div> 13 <script> 14 var imgBox = document.getElementsByClassName('imgBox')[0]; 15 var reader = new FileReader(); //建立文件讀取對象 16 var inp = document.getElementsByTagName('input')[0]; //獲取文件源 17 inp.onchange = function(){ //input域發生改變後觸發文件讀取 18 reader.readAsDataURL(inp.files[0]); //使用文件讀取對象讀取圖片爲base64編碼 19 } 20 reader.onload = function(e){ //當圖片讀取成功後觸發 21 var img = new Image(); //建立img對象 22 img.src = e.target.result; //給img對象添加緩存中的bese64位編碼的圖片數據(異步) 23 img.onload = function(e){ //圖片數據加載完成之後 24 if(this.width > this.height){ //當圖片的寬度大於高度 25 img.style.width = '100%'; //是:設置圖片寬度100%,實現圖片所有預覽 26 }else{ 27 img.style.height = '100%';//否:設置圖片高度100%,實現圖片所有預覽 28 } 29 imgBox.style.backgroundColor = '#000'; 30 imgBox.innerHTML = null; 31 imgBox.appendChild(img); 32 } 33 } 34 </script>
在FileReader.onprogress事件對象中有兩個屬性loaded和total,loaded表示當前文件讀取大小,total表示文件總體大小,而且在讀取時會持續觸發更新最新讀取狀態的數據,根據FileReader.onprogress事件就能夠實現文件加載進度條的動畫效果了,可是因爲FileReader是h5的API在IE中最低兼容到10版本,因此須要根據具體的項目和兼容性來設計交互。web
1 //css 2 .progress{ 3 position: relative; 4 margin-top: 5px; 5 width: 300px; 6 height: 20px; 7 border: 1px solid #300; 8 } 9 .progressText{ 10 display: inline-block; 11 position: absolute; 12 width: 300px; 13 height: 20px; 14 text-align: center; 15 font-size: 10px; 16 line-height: 20px; 17 } 18 .progressSpan{ 19 display: inline-block; 20 /* width: 200px; */ 21 height: 20px; 22 background-color: #f0f; 23 } 24 //html 25 <input type="file" name=""> 26 <!-- 文件加載進度條 --> 27 <div class="progress"> 28 <span class="progressText"></span> 29 <span class="progressSpan"></span> 30 </div> 31 //js 32 //獲取文件源(全部功能實現的公共代碼區) 33 var inp = document.getElementsByTagName('input')[0]; //獲取文件源 34 var reader = new FileReader(); //建立文件讀取對象 35 // fileReader實現圖片加載進度條 36 var progressSpanObj = document.getElementsByClassName('progressSpan')[0]; 37 var progressTextObj = document.getElementsByClassName('progressText')[0]; 38 inp.onchange = function(){ 39 reader.readAsArrayBuffer(inp.files[0]); 40 } 41 reader.onloadstart = function(e){ //開始讀取文件時觸發 42 progressTextObj.innerText = "正在讀取文件(0%)..."; 43 } 44 reader.onprogress = function(e){ //讀取進度事件 45 console.log(Math.round(e.loaded / e.total * 100)); 46 var precent = Math.round(e.loaded / e.total * 100); 47 progressSpanObj.style.width = precent / 100 * 300 + 'px'; 48 progressTextObj.innerText = '正在讀取文件(' + precent + '%)...'; 49 } 50 reader.onload = function(e){ 51 progressTextObj.innerText = '文件讀取完成(100%)'; 52 } 53 reader.onerror = function(e){ 54 progressTextObj.innerText = "文件讀取出錯誤(~0v0~)"; 55 }
經過input-type[file]獲取的文件對象上有這樣幾個數據:ajax
inputDom.files[0];//獲取File對象(在onchange事件後獲取)
File對象說明手冊(MDN):https://developer.mozilla.org/zh-CN/docs/Web/API/Filechrome
File.slice()方法說明手冊(MDN):https://developer.mozilla.org/zh-CN/docs/Web/API/Blob/slice緩存
1 <!--樣式同上面的進度條實例同樣,這裏就不添加了--> 2 <input type="file" name=""> 3 <!-- 文件加載進度條 --> 4 <div class="progress"> 5 <span class="progressText"></span> 6 <span class="progressSpan"></span> 7 </div> 8 //獲取文件源(全部功能實現的公共代碼區) 9 var inp = document.getElementsByTagName('input')[0]; //獲取文件源 10 var reader = new FileReader(); //建立文件讀取對象 11 // fileReader實現圖片加載進度條 12 var progressSpanObj = document.getElementsByClassName('progressSpan')[0]; 13 var progressTextObj = document.getElementsByClassName('progressText')[0]; 14 //file.slice(起始字節,終止字節)與FileReader實現文件切片讀取 15 function PartFileReader(files,type,event){ 16 this.files = files;//inputObj.files[0] 17 this.type = type; //配置FileReader讀取文件的方法 18 this.event = event;//配置讀取文件時須要觸發的事件 19 this.total = files.size;//獲取文件大小 20 this.step = 1024 * 1024;//1MB(單片大小/一兆) 21 this.loaded = 0; //文件當前讀取進度 22 this.reader = new FileReader(); //實際讀取文件的FileReader對象實例 23 this.abort = this.reader.abort; //中斷文件讀取(能夠經過中斷文件讀取事件保留切片數據,實現下一次能夠在原讀取位置繼續開始讀取) 24 this.readPartFile(this.loaded); //開啓讀取文件 25 this.bindEvent();//綁定FileReader文件讀取 26 } 27 //給切片讀取對象原型上添加FileReader獲取讀取類型,開啓讀取文件 28 PartFileReader.prototype.readPartFile = function(start){ 29 if(this.files.slice){ 30 var file = this.files.slice(start,this.loaded + this.step); 31 switch(this.type){ 32 case 'readAsBinaryString' : 33 this.reader.readAsBinaryString(file); 34 break; 35 case 'readAsDataURL' : 36 this.reader.readAsDataURL(file); 37 break; 38 case 'readAsArrayBuffer' : 39 this.reader.readAsArrayBuffer(file); 40 break; 41 case 'readAsText' : 42 this.readAsText(file); 43 break; 44 } 45 } 46 } 47 //給切片讀取對象原型上綁定FileReader對象事件 48 PartFileReader.prototype.bindEvent = function(){ 49 var self = this; 50 this.reader.onloadstart = function(e){ 51 self.event.loadStart && self.event.loadStart.call(this,e); 52 } 53 this.reader.onprogress = function(e){ 54 self.event.progress && self.event.progress.call(this,e); 55 } 56 this.reader.onload = function(e){ 57 // 切片讀取文件有別於非切片讀取,切片讀取的文件讀取狀態須要在每一個切片讀取成功後再刷新讀取進度 58 self.loaded += e.loaded; 59 self.event.load && self.event.load.call(this,e,self.loaded,self.total); 60 if(self.loaded < self.total){ 61 self.readPartFile(self.loaded); 62 } 63 } 64 this.reader.onloadend = function(e){ 65 self.event.loadend && self.event.loadend.call(this,e); 66 } 67 this.reader.onabort = function(e){ 68 self.event.abort && self.event.abort.call(this,e); 69 } 70 } 71 //調用文件切片讀取對象,配置FileReader事件函數 72 inp.onchange = function(){ 73 var reader = new PartFileReader(inp.files[0],'readAsArrayBuffer',{ 74 loadStart:function(e){ 75 progressTextObj.innerText = "正在讀取文件(0%)..."; 76 }, 77 progress:function(e){}, 78 load:function(e,loaded,total){ 79 // 若是在讀取的基礎上寫上傳的話在這裏獲取讀取成功的文件切片 80 // e.target.result //當前文件切片的數據 81 // (可是千萬別在這裏直接寫網絡請求,本地讀取的速度遠遠大於網絡請求,直接請求一個文件就會在瞬間發起大量請求) 82 // (最好的處理方式是將切片數據寫入一個有序列表中,而後經過控制網絡請求數量來實現) 83 var precent = Math.round(loaded / total * 100); 84 progressSpanObj.style.width = precent / 100 * 300 + 'px'; 85 if(precent < 100){ 86 progressTextObj.innerText = '正在讀取文件(' + precent + '%)...'; 87 }else if(precent == 100){ 88 progressTextObj.innerText = '文件讀取完成(100%)'; 89 } 90 }, 91 loadend:function(e){}, 92 abort:function(e){}, 93 error:function(e){ 94 progressTextObj.innerText = "文件讀取出錯誤(~0v0~)"; 95 } 96 }) 97 }