HTML5 file API加canvas實現圖片前端JS壓縮並上傳

1、圖片上傳前端壓縮的現實意義

對於大尺寸圖片的上傳,在前端進行壓縮除了省流量外,最大的意義是極大的提升了用戶體驗。php

這種體驗包括兩方面:html

  1. 因爲上傳圖片尺寸比較小,所以上傳速度會比較快,交互會更加流暢,同時大大下降了網絡異常致使上傳失敗風險。
  2. 最最重要的體驗改進點:省略了圖片的再加工成本。不少網站的圖片上傳功能都會對圖片的大小進行限制,尤爲是頭像上傳,限制5M或者2M之內是很是常見的。而後如今的數碼設備拍攝功能都很是出衆,一張原始圖片超過2M幾乎是標配,此時若是用戶想把手機或相機中的某個得意圖片上傳做爲本身的頭像,就會遇到由於圖片大小限制而不能上傳的窘境,不得不對圖片進行再處理,而這種體驗其實很是很差的。若是能夠在前端進行壓縮,則理論上對圖片尺寸的限制是沒有必要的。

2、圖片前端JS壓縮並上傳功能體驗

特地製做了一個圖片前端壓縮並上傳的完整demo,您能夠狠狠的點擊這裏:使用canvas在前端壓縮圖片並上傳demo前端

進入demo會看到一個相貌平平的文件輸入框:web

相貌平平

啊,不對,應該是這張圖:ajax

相貌平平文件選擇框

點擊文件選擇框,咱們不妨選一張尺寸比較大的圖片,例以下面這種2M多的釣魚收穫照:算法

上傳演示使用的圖片

因而圖片歘歘歘地傳上去了:
上傳相關信息截圖canvas

此時咱們點擊最終上傳完畢的圖片地址,會發現原來2M多3000多像素寬的圖片被限制爲400像素寬了:
圖片縮小後在瀏覽器中的預覽效果圖後端

保存到本地會發現圖片尺寸已經變成只有70K了:
保存到本地顯示的圖片尺寸瀏覽器

以上就是圖片前端壓縮並上傳demo的完整演示。網絡

3、HTML5 file API加canvas實現圖片前端JS壓縮

要想使用JS實現圖片的壓縮效果,原理其實很簡單,核心API就是使用canvasdrawImage()方法。

canvasdrawImage()方法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上原理示意:
Canvas drawimage()原理示意

對於本文的圖片壓縮,須要用的是是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表示導出的圖片質量,只要導出爲jpgwebp格式的時候此參數纔有效果,默認值是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頁面的左側,若是對其餘交互代碼也敢興趣,請參考頁面源代碼。

4、結束語

就在幾個月前剛寫過一篇文章「使用canvas在前端實現圖片水印合成」,實際上所使用的技術和套路和本文是一模一樣的,也是「圖片→canvas水印→圖片」三步曲,區別在於水印合成是連續執行兩次context.drawImage()方法,一次是原圖一次水印圖片,以及最後轉換成圖片的時候什麼是toDataURL()方法,其餘代碼邏輯和原理都是同樣的。

由此及彼,利用一樣的原理和代碼邏輯,咱們還能夠實現其它不少之前前端不太好實現的功能,比方說圖片的真剪裁效果,所謂「真剪裁」指不是使用個overflow:hidden或者clip這些CSS屬性的「僞剪裁」,而是真正意義上就這麼大區域圖片信息。甚至配合一些前端算法,咱們能夠直接在前端進行人臉識別,圖片自動美化等一系列功能再上傳等等。

原理都是同樣的,都是利用canvas做爲中間媒介進行處理。

好,以上就是本文的所有內容,感謝閱讀,歡迎糾錯,歡迎交流!

相關文章
相關標籤/搜索