第0052期•2020.03.20 發行
ISSN 2077-8813

首頁 >技術論壇

照片上傳前端壓縮

作者:洪嘉駿 / 臺灣大學計算機及資訊網路中心程式設計組幹事

前言
現代人或多或少都有上傳照片的經驗,像是透過通訊軟體或社群網站分享照片,又或是在某個網路平台上傳自己的大頭貼等。一般來說,在沒有特別要求畫質的情況下,系統會自動縮小這些照片的尺寸,以降低儲存、傳輸的成本。以下就以網頁應用程式為例,介紹前端壓縮的方法,並探討其優缺點。(本文的壓縮是指縮小影像尺寸)

前端壓縮是指消耗使用者端的運算資源來壓縮照片,壓縮後,才將照片傳輸至伺服器。另外一種壓縮方式是先將照片傳送到後端,再由伺服器壓縮照片,稱為後端壓縮。本文要介紹的是前端壓縮。

前端
前端壓縮的其中一種方法,是透過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
  • CanvasRenderingContext2D.drawImage()
  • FileReader
  • Firefox 3.6 支援 File API
  • Google Chrome 7 支援 File API
  • Internet Explorer 10 支援 File API