作者:洪嘉駿 / 臺灣大學計算機及資訊網路中心程式設計組幹事
介紹如何以HTML canvas來壓縮使用者上傳的照片。
前言
現代人或多或少都有上傳照片的經驗,像是透過通訊軟體或社群網站分享照片,又或是在某個網路平台上傳自己的大頭貼等。一般來說,在沒有特別要求畫質的情況下,系統會自動縮小這些照片的尺寸,以降低儲存、傳輸的成本。以下就以網頁應用程式為例,介紹前端壓縮的方法,並探討其優缺點。(本文的壓縮是指縮小影像尺寸)
前端壓縮是指消耗使用者端的運算資源來壓縮照片,壓縮後,才將照片傳輸至伺服器。另外一種壓縮方式是先將照片傳送到後端,再由伺服器壓縮照片,稱為後端壓縮。本文要介紹的是前端壓縮。
前端
前端壓縮的其中一種方法,是透過HTML的canvas來壓縮照片。CanvasRenderingContext2D提供了drawImage()方法,讓我們可以將指定的HTML image元素(<img>),繪製到canvas上,同時還可以指定繪製的大小,我們就是藉由指定繪製大小的功能,來達到縮小照片的目的。
但問題來了,使用者是透過<input type="file">來選擇欲上傳的照片,我們該如何將<input>內的檔案轉成<img>?
這時候,我們就需要用到瀏覽器的File API來讀取<input type="file">內的照片。我們可以透過FileReader.readAsDataURL()來讀取使用者選好的圖片。
readAsDataURL會回傳Data URL格式的資料,並且將我們的照片以base64編碼。舉例來說,假設上傳一張jpg檔,readAsDataURL回傳的資料大致如下:
data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ...
(逗號之前是Data URL的資訊,逗號之後才是base64編碼後的照片)
接著,我們可以將Data URL格式的資料傳給<img>的src屬性如:
<img src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ...">
這樣就可以將使用者選好的照片變成HTML image元素。
有了HTML image元素,我們就可以用CanvasRenderingContext2D.drawImage(image, dx, dy, dWidth, dHeight),將這個image元素以指定大小,重新繪製到canvas上。
繪製好後,我們再調用Canvas.toDataURL(),就能將canvas的內容輸出成Data URL格式。這時候我們的照片已經被縮小、並以base64編碼。例如:
data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD…
接下來要做的就是將base64編碼後的照片傳回伺服器,可以選擇用AJAX傳,也可以選擇以一般的form傳。
以一般的form傳的話,可以將這串base64字串填入hidden的input(<input name="img_DataUrl" type="text" hidden />)。注意,不是原本的<input type="file">。<input type="file">是唯讀的。
到此為止,照片已經在前端壓縮完畢,並且將base64編碼的照片傳給後端。接下來後端要做的事就是將base64的照片解碼,並儲存。
後端
以下以C# ASP.NET MVC示範後端如何將base64的照片解碼並儲存。(本範例選擇讓前端傳送完整的Data URL,所以本範例中,後端收到的資料是Data URL格式,必須先去掉開頭的「data:image/jpeg;base64, 」,剩下的才是base64編碼後的照片)
圖一 以C# ASP.NET MVC示範後端如何將base64的照片解碼並儲存
以上就是上傳照片前端壓縮的方法。
結語
前端壓縮對使用者來說,可以節省傳輸時的流量,因為照片是先壓縮,才傳送。如果使用者的網路是按流量計費的,那前端壓縮就可以幫使用者省下珍貴的流量。
除了幫使用者省流量外,前端壓縮還可以減少伺服器的工作量,因為壓縮時的運算資源由使用者負擔,所以同樣數量的伺服器,將可以服務更多使用者。
前端壓縮也有一些缺點,本文所介紹的前端壓縮方法會用到HTML canvas和File API,這兩個功能在太舊的瀏覽器中可能不支援。尤其是File API,要到Firefox 3.6(2010年釋出)、Google Chrome 7(2010年釋出)和IE 10(2012年釋出)才支援。
參考資料
- Image resize before upload
https://stackoverflow.com/questions/961913/image-resize-before-upload/8397789#8397789
- CanvasRenderingContext2D.drawImage()
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage
- FileReader
https://developer.mozilla.org/en-US/docs/Web/API/FileReader
- Firefox 3.6 支援 File API
https://website-archive.mozilla.org/www.mozilla.org/firefox_releasenotes/en-us/firefox/3.6/releasenotes/
- Google Chrome 7 支援 File API
https://chromereleases.googleblog.com/2010/10/stable-channel-update.html
- Internet Explorer 10 支援 File API
https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/dev-guides/hh673542(v=vs.85)