實際生產中常常遇到這樣的場景:爲減少服務器壓力,上傳附件尤爲是圖片的時候,每每須要限制上傳文件的大小。而限制的方案也有兩種,一種就是限制用戶可上傳的文件大小,由用戶來選擇上傳的文件和若是文件過大由用戶自行進行壓縮裁剪;另外一種就是由服務進行圖片的壓縮和大小控制而後再上傳到服務器。這裏主要介紹的是第二種方案。javascript
前邊有介紹過證書的生成和下載,其中就有證書的壓縮和打包的相關操做,感興趣的能夠看下本人的那篇文章。這裏一樣是採用的該原理,步驟以下:html
圖片文件-->文件流(base64位編碼)-->canvas-->壓縮-->生成壓縮後的文件-->上傳。vue
這裏的壓縮過程,作了相應的優化。優化方案有兩種,一種是重複壓縮,一種是計算比例壓縮。java
而因爲壓縮比和文件大小並非正比例關係,全部能夠保險起見再乘以一個係數。好比:quality: 1024*0.7/fileObj.size(0.7是保險係數,1024是限制大小1M的意思,可根據我的須要自行調整參數,也能夠封裝成接口參數統一修改)node
這裏還自行封裝了一個進度組件,使用的是原生js。git
代碼和相關注釋以下:github
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8"> 5 <title>文件壓縮上傳</title> 6 <script type="text/javascript"> 7 /* 8 三個參數 9 file:一個是文件(類型是圖片格式), 10 w:一個是文件壓縮的後寬度,寬度越小,字節越小 11 objDivOrCallback:一個是容器或者回調函數 12 photoCompress() 13 */ 14 function photoCompress(file,w,objDivOrCallback) { 15 var ready = new FileReader() 16 /*開始讀取指定的Blob對象或File對象中的內容. 當讀取操做完成時,readyState屬性的值會成爲DONE,若是設置了onloadend事件處理程序,則調用之.同時,result屬性中將包含一個data: URL格式的字符串以表示所讀取文件的內容.*/ 17 ready.readAsDataURL(file) 18 ready.onload = function() { 19 var re = this.result 20 canvasDataURL(re, w, objDivOrCallback) 21 } 22 } 23 function canvasDataURL(path, obj, callback) { 24 var img = new Image() 25 img.src = path 26 img.onload = function(){ 27 var that = this 28 // 默認按比例壓縮 29 var w = that.width, 30 h = that.height, 31 scale = w / h 32 w = obj.width || w 33 h = obj.height || (w / scale) 34 var quality = 0.7 // 默認圖片質量爲0.7 35 //生成canvas 36 var canvas = document.createElement('canvas') 37 var ctx = canvas.getContext('2d') 38 // 建立屬性節點 39 var anw = document.createAttribute("width") 40 anw.nodeValue = w 41 var anh = document.createAttribute("height") 42 anh.nodeValue = h 43 canvas.setAttributeNode(anw) 44 canvas.setAttributeNode(anh) 45 ctx.drawImage(that, 0, 0, w, h) 46 // 圖像質量 47 if(obj.quality && obj.quality <= 1 && obj.quality > 0) { 48 quality = obj.quality 49 } 50 // quality值越小,所繪製出的圖像越模糊 51 var base64 = canvas.toDataURL('image/jpeg', quality) 52 // 這裏不能直接quality: 0.2,由於這樣就至關於仍是在原來的大小的基礎上壓縮 53 var bl = convertBase64UrlToBlob(base64) 54 // 若是還大於1M,繼續壓縮--代碼待優化,能夠減去重複生成文件和轉碼的過程 55 if (bl.size/1024 > 1025) { 56 // 其實也能夠在這裏直接寫一個匹配壓縮比直到大小小於1的方法 57 photoCompress(bl, { 58 quality: 0.5 * obj.quality 59 }, callback) 60 } else { 61 callback(bl) 62 } 63 // 回調函數返回base64的值--改成返回文件對象 64 // callback(base64) 65 } 66 } 67 /** 68 * 將以base64的圖片url數據轉換爲Blob 69 * @param urlData 70 * 用url方式表示的base64圖片數據 71 */ 72 function convertBase64UrlToBlob(urlData) { 73 var arr = urlData.split(','), mime = arr[0].match(/:(.*?);/)[1], 74 bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n) 75 while(n--) { 76 u8arr[n] = bstr.charCodeAt(n) 77 } 78 return new Blob([u8arr], {type:mime}) 79 } 80 81 82 var xhr 83 //上傳文件方法 84 function UpladFile() { 85 var fileObj = document.getElementById("file").files[0] // js 獲取文件對象 86 var url = "http://pxjy.api.test.nercel.cn/file/publicFile/upload" // 接收上傳文件的後臺地址 87 88 var form = new FormData() // FormData 對象 89 90 if(fileObj.size/1024 > 1025) { //大於1M,進行壓縮上傳 91 photoCompress(fileObj, { 92 // 這裏還有一種方案,那就是這裏的quality改成計算壓縮比(因爲壓縮比和文件大小並非正比例關係,全部能夠保險起見再乘以一個係數) 93 // 壓縮比計算的方案:quality: 1024*0.7/fileObj.size--0.7是保險係數--這些參數能夠進一步封裝 94 quality: 0.2 95 // }, function(base64Codes){ 96 // 修改成返回文件對象 97 }, function(bl){ 98 //console.log("壓縮後:" + base.length / 1024 + " " + base); 99 // var bl = convertBase64UrlToBlob(base64Codes) 100 // form.append("file", bl, "file_"+Date.parse(new Date())+".jpg"); // 文件對象 101 form.append("multipartFile", bl, "file_"+Date.parse(new Date())+".jpg") // 文件對象 102 xhr = new XMLHttpRequest() // XMLHttpRequest 對象 103 xhr.open("post", url, true) //post方式,url爲服務器請求地址,true 該參數規定請求是否異步處理。 104 xhr.setRequestHeader("enctype", "multipart/form-data") // 設置請求頭 105 xhr.setRequestHeader("Authorization", "Bearer 8d782bb1-768f-4fa7-80d2-5e2b6d6a6f64") // 設置請求頭 106 // open後才能夠設置頭 107 xhr.onload = uploadComplete //請求完成 108 xhr.onerror = uploadFailed //請求失敗 109 110 xhr.upload.onprogress = progressFunction//【上傳進度調用方法實現】 111 xhr.upload.onloadstart = function(){//上傳開始執行方法 112 ot = new Date().getTime() //設置上傳開始時間 113 oloaded = 0//設置上傳開始時,以上傳的文件大小爲0 114 }; 115 116 xhr.send(form) //開始上傳,發送form數據 117 }) 118 } else { //小於等於1M 原圖上傳 119 // form.append("file", fileObj) // 文件對象 120 form.append("multipartFile", fileObj) // 文件對象 121 xhr = new XMLHttpRequest() // XMLHttpRequest 對象 122 xhr.open("post", url, true) //post方式,url爲服務器請求地址,true 該參數規定請求是否異步處理。 123 xhr.setRequestHeader("enctype", "multipart/form-data") // 設置請求頭 124 xhr.setRequestHeader("Authorization", "Bearer 8d782bb1-768f-4fa7-80d2-5e2b6d6a6f64") // 設置請求頭 125 // open後才能夠設置頭 126 xhr.onload = uploadComplete //請求完成 127 xhr.onerror = uploadFailed //請求失敗 128 129 xhr.upload.onprogress = progressFunction//【上傳進度調用方法實現】 130 xhr.upload.onloadstart = function() {//上傳開始執行方法 131 ot = new Date().getTime() //設置上傳開始時間 132 oloaded = 0//設置上傳開始時,以上傳的文件大小爲0 133 } 134 135 xhr.send(form) //開始上傳,發送form數據 136 } 137 } 138 139 //上傳成功響應 140 function uploadComplete(evt) { 141 //服務斷接收完文件返回的結果 142 var data = JSON.parse(evt.target.responseText) 143 if(data.code === 200) { 144 uploadSuccess() 145 } else { 146 uploadFailed() 147 } 148 149 } 150 //上傳失敗 151 function uploadFailed(evt) { 152 alert("上傳失敗!") 153 } 154 //上傳成功 155 function uploadSuccess(evt) { 156 alert("上傳成功!") 157 } 158 //取消上傳 159 function cancleUploadFile(){ 160 xhr.abort() 161 } 162 163 //上傳進度實現方法,上傳過程當中會頻繁調用該方法 164 function progressFunction(evt) { 165 var progressBar = document.getElementById("progressBar") 166 var percentageDiv = document.getElementById("percentage") 167 // event.total是須要傳輸的總字節,event.loaded是已經傳輸的字節。若是event.lengthComputable不爲真,則event.total等於0 168 if (evt.lengthComputable) {// 169 progressBar.max = evt.total 170 progressBar.value = evt.loaded 171 percentageDiv.innerHTML = Math.round(evt.loaded / evt.total * 100) + "%" 172 } 173 var time = document.getElementById("time") 174 var nt = new Date().getTime()//獲取當前時間 175 var pertime = (nt-ot)/1000 //計算出上次調用該方法時到如今的時間差,單位爲s 176 ot = new Date().getTime() //從新賦值時間,用於下次計算 177 var perload = evt.loaded - oloaded //計算該分段上傳的文件大小,單位b 178 oloaded = evt.loaded//從新賦值已上傳文件大小,用如下次計算 179 //上傳速度計算 180 var speed = perload/pertime//單位b/s 181 var bspeed = speed 182 var units = 'b/s'//單位名稱 183 if(speed/1024>1) { 184 speed = speed/1024 185 units = 'k/s' 186 } 187 if(speed/1024>1) { 188 speed = speed/1024 189 units = 'M/s' 190 } 191 speed = speed.toFixed(1) 192 //剩餘時間 193 var resttime = ((evt.total-evt.loaded)/bspeed).toFixed(1) 194 time.innerHTML = ',速度:'+speed+units+',剩餘時間:'+resttime+'s' 195 if(bspeed==0) time.innerHTML = '上傳已取消' 196 } 197 </script> 198 </head> 199 <body> 200 <progress id="progressBar" value="0" max="100" style="width: 300px;"></progress> 201 <span id="percentage"></span><span id="time"></span> 202 <br /><br /> 203 <input type="file" id="file" name="myfile" accept="image/x-png, image/jpg, image/jpeg, image/gif"/> 204 <input type="button" onclick="UpladFile()" value="上傳" /> 205 <input type="button" onclick="cancleUploadFile()" value="取消" /> 206 </body> 207 </html>
此處是借鑑網上思路的基礎上的我的修改完善後的代碼, 而且有待有時間的時候作進一步封裝優化和封裝成npm組件以及vue組件。算法
代碼git地址:數據庫
png的簡介
什麼是png:
PNG的全稱叫便攜式網絡圖型(Portable Network Graphics)是目前最流行的網絡傳輸和展現的圖片格式,緣由有以下幾點:
無損壓縮
:PNG圖片採起了基於LZ77派生算法對文件進行壓縮,使得它壓縮比率更高,生成的文件體積更小,而且不損失數據。
體積小
:它利用特殊的編碼方法標記重複出現的數據,使得一樣格式的圖片,PNG圖片文件的體積更小。網絡通信中因受帶寬制約,在保證圖片清晰、逼真的前提下,優先選擇PNG格式的圖片。
支持透明效果
:PNG支持對原圖像定義256個透明層次,使得圖像的邊緣能與任何背景平滑融合,這種功能是GIF和JPEG沒有的。
當初就是由於png的透明特性纔開始喜歡它的。
png的類型:
PNG 8
:PNG 8中的8,其實指的是8bits,至關於用2^8(2的8次方)大小來存儲一張圖片的顏色種類,2^8等於256,也就是說PNG 8能存儲256種顏色,一張圖片若是顏色種類不多,將它設置成PNG 8得圖片類型是很是適合的。
PNG 24
:PNG 24中的24,至關於3乘以8 等於 24,就是用三個8bits分別去表示 R(紅)、G(綠)、B(藍)。R(0~255),G(0~255),B(0~255),能夠表達256乘以256乘以256=16777216種顏色的圖片,這樣PNG 24就能比PNG 8表示色彩更豐富的圖片。可是所佔用的空間相對就更大了。
PNG 32
:PNG 32中的32,至關於PNG 24 加上 8bits的透明顏色通道,就至關於R(紅)、G(綠)、B(藍)、A(透明)。R(0~255),G(0~255),B(0~255),A(0~255)。比PNG 24多了一個A(透明),也就是說PNG 32能表示跟PNG 24同樣多的色彩,而且還支持256種透明的顏色,能表示更加豐富的圖片顏色類型。
png圖片的數據編碼:
PNG圖片的數據結構其實跟http請求的結構很像,都是一個數據頭,後面跟着不少的數據塊,以下圖所示:
使用16進制編碼打開png圖片,部分編碼示例以下:
8950 4e47 0d0a 1a0a
:這個是PNG圖片的頭,全部的PNG圖片的頭都是這一串編碼,圖片軟件經過這串編碼斷定這個文件是否是PNG格式的圖片。
0000 000d
:是iHDR數據塊的長度,爲13。
4948 4452
:是數據塊的type,爲IHDR,以後緊跟着是data。
0000 0292
:是圖片的寬度。
0000 024e
:是高度。
以此類推,每一段十六進制編碼就表明着一個特定的含義。感興趣的能夠自行百度。
因此,顏色重複度越大的、越接近的(漸變的顏色或透明度等),編碼重複度就越大,就越容易壓縮。
壓縮原理:
png圖片用差分編碼(Delta encoding)對圖片進行預處理,處理每個的像素點中每條通道的值。
壓縮階段會將預處理階段獲得的結果進行Deflate壓縮,它由 Huffman 編碼 和 LZ77壓縮構成。
壓縮後的結果就是一串處理後的編碼,保存到數據庫中,佔用空間會小不少,在使用的時候,再進行逆向解析渲染。
具體代碼暫無。