一、圖片上傳前端壓縮的現實意義
對於大尺寸圖片的上傳,在前端進行壓縮除了省流量外,最大的意義是極大的提高了用戶體驗。
這種體驗包括兩方面:
- 由於上傳圖片尺寸比較小,因此上傳速度會比較快,交互會更加流暢,同時大大降低了網絡異常導致上傳失敗風險。
- 最最重要的體驗改進點:省略了圖片的再加工成本。很多網站的圖片上傳功能都會對圖片的大小進行限制,尤其是頭像上傳,限制5M或者2M以內是非常常見的。然后現在的數碼設備拍攝功能都非常出眾,一張原始圖片超過2M幾乎是標配,此時如果用戶想把手機或相機中的某個得意圖片上傳作為自己的頭像,就會遇到因為圖片大小限制而不能上傳的窘境,不得不對圖片進行再處理,而這種體驗其實非常不好的。如果可以在前端進行壓縮,則理論上對圖片尺寸的限制是沒有必要的。
二、圖片前端JS壓縮並上傳功能體驗
特意制作了一個圖片前端壓縮並上傳的完整demo,您可以狠狠的點擊這里:使用canvas在前端壓縮圖片並上傳demo
進入demo會看到一個相貌平平的文件輸入框:
啊,不對,應該是這張圖:
點擊文件選擇框,我們不妨選一張尺寸比較大的圖片,例如下面這種2M多的釣魚收獲照:
於是圖片歘歘歘地傳上去了:
此時我們點擊最終上傳完畢的圖片地址,會發現原來2M多3000多像素寬的圖片被限制為400像素寬了:
保存到本地會發現圖片尺寸已經變成只有70K了:
以上就是圖片前端壓縮並上傳demo的完整演示。
三、HTML5 file API加canvas實現圖片前端JS壓縮
要想使用JS實現圖片的壓縮效果,原理其實很簡單,核心API就是使用canvas
的drawImage()
方法。
canvas
的drawImage()
方法API如下:
context.drawImage(img, dx, dy); context.drawImage(img, dx, dy, dWidth, dHeight); context.drawImage(img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
后面最復雜的語法雖然看上去有9大參數,但不用慌,實際上可以看出就3個參數:
- img
- 就是圖片對象,可以是頁面上獲取的DOM對象,也可以是虛擬DOM中的圖片對象。
- dx, dy, dWidth, dHeight
-
表示在
canvas
畫布上規划處一片區域用來放置圖片,dx, dy
為canvas元素的左上角坐標,dWidth, dHeight
指canvas元素上用在顯示圖片的區域大小。如果沒有指定sx,sy,sWidth,sHeight
這4個參數,則圖片會被拉伸或縮放在這片區域內。 - sx,sy,swidth,sheight
-
這4個坐標是針對圖片元素的,表示圖片在
canvas
畫布上顯示的大小和位置。sx,sy
表示圖片上sx,sy
這個坐標作為左上角,然后往右下角的swidth,sheight
尺寸范圍圖片作為最終在canvas上顯示的圖片內容。
drawImage()
方法有一個非常怪異的地方,大家一定要注意,那就是5參數和9參數里面參數位置是不一樣的,這個和一般的API有所不同。一般API可選參數是放在后面。但是,這里的drawImage()
9個參數時候,可選參數sx,sy,swidth,sheight
是在前面的。如果不注意這一點,有些表現會讓你無法理解。
下圖為MDN上原理示意:
對於本文的圖片壓縮,需要用的是是5個參數語法。舉個例子,一張圖片(假設圖片對象是img
)的原始尺寸是4000*3000,現在需要把尺寸限制為400*300大小,很簡單,原理如下代碼示意:
var canvas = document.createElement('canvas'); var context = canvas.getContext('2d'); canvas.width = 400; canvas.height = 300; // 核心JS就這個 context.drawImage(img,0,0,400,300);
把一張大的圖片,直接畫在一張小小的畫布上。此時大圖片就天然變成了小圖片,壓縮就這么實現了,是不是簡單的有點超乎想象。
當然,若要落地於實際開發,我們還需要做些其他的工作,就是要解決圖片來源和圖片去向的問題。
1. 如何把系統中圖片呈現在瀏覽器中?
HTML5 file API可以讓圖片在上傳之前直接在瀏覽器中顯示,通常使用FileReader
方法,代碼示意如下:
var reader = new FileReader(), img = new Image(); // 讀文件成功的回調 reader.onload = function(e) { // e.target.result就是圖片的base64地址信息 img.src = e.target.result; }; eleFile.addEventListener('change', function (event) { reader.readAsDataURL(event.target.files[0]); });
於是,包含圖片信息的context.drawImage()
方法中的img
圖片就有了。
2. 如果把canvas畫布轉換成img圖像canvas
天然提供了2個轉圖片的方法,一個是:
- canvas.toDataURL()方法
-
語法如下:
canvas.toDataURL(mimeType, qualityArgument)
可以把圖片轉換成base64格式信息,純字符的圖片表示法。
其中:
mimeType
表示canvas
導出來的base64
圖片的類型,默認是png格式,也即是默認值是'image/png'
,我們也可以指定為jpg格式'image/jpeg'
或者webp等格式。file
對象中的file.type
就是文件的mimeType類型,在轉換時候正好可以直接拿來用(如果有file對象)。qualityArgument
表示導出的圖片質量,只要導出為jpg
和webp
格式的時候此參數才有效果,默認值是0.92
,是一個比較合理的圖片質量輸出參數,通常情況下,我們無需再設定。 - canvas.toBlob()方法
-
語法如下:
canvas.toBlob(callback, mimeType, qualityArgument)
可以把canvas轉換成Blob文件,通常用在文件上傳中,因為是二進制的,對后端更加友好。
和
toDataURL()
方法相比,toBlob()
方法是異步的,因此多了個callback
參數,這個callback
回調方法默認的第一個參數就是轉換好的blob
文件信息,本文demo的文件上傳就是將canvas
圖片轉換成二進制的blob
文件,然后再ajax
上傳的,代碼如下:// canvas轉為blob並上傳 canvas.toBlob(function (blob) { // 圖片ajax上傳 var xhr = new XMLHttpRequest(); // 開始上傳 xhr.open("POST", 'upload.php', true); xhr.send(blob); });
於是,經過“圖片→canvas壓縮→圖片”三步曲,我們完成了圖片前端壓縮並上傳的功能。
更加完整的核心代碼請參見demo頁面的左側,如果對其他交互代碼也敢興趣,請參考頁面源代碼。