檔案上傳系統設計 (7) —— 客戶端預處理的威力
在上一篇中,我們提升了用戶的上傳體驗。但如果用戶上傳的是原始的 4K 照片(可能 10MB 以上),而我們只需要一個用於顯示的縮圖,直接傳送到伺服器會造成巨大的頻寬浪費。
本篇將介紹如何利用瀏覽器的運算能力,在檔案離開用戶電腦前先進行「預處理」。
一、 圖片壓縮:省下 90% 的頻寬
我們可以使用 HTML5 的 Canvas API 在前端直接縮小圖片尺寸並降低品質。
實作思路:
- 使用
URL.createObjectURL讀取檔案。 - 將圖片繪製到畫布上。
- 透過
canvas.toBlob()或canvas.toDataURL()導出壓縮後的版本。
javascript
async function compressImage(file, { maxWidth = 800, quality = 0.7 }) {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = (e) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement("canvas");
let width = img.width;
let height = img.height;
// 計算比例縮放
if (width > maxWidth) {
height = (maxWidth / width) * height;
width = maxWidth;
}
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, width, height);
canvas.toBlob((blob) => resolve(blob), "image/jpeg", quality);
};
img.src = e.target.result;
};
reader.readAsDataURL(file);
});
}二、 解決幻之旋轉:EXIF 資訊處理
你有遇過上傳的手機照片「自動躺平」嗎?這是因為照片中含有 EXIF (Exchangeable image file format) 中的 Orientation 標籤,而以前的瀏覽器在 Canvas 繪圖時常會忽略它。
解決方案:
現代瀏覽器在 drawImage 時大多已能自動處理,但若遇到舊環境,可以使用 browser-image-compression 或 blueimp-load-image 等函式庫來確保圖片方向正確。
三、 秒傳的前哨站:前端哈希 (Hash) 計算
在「效能優化篇」我們提過「秒傳」。為了實現秒傳,前端必須先計算檔案的 Hash 值傳給後端比對。
對於大檔案,直接計算會讓 JS 主執行緒卡死。推薦使用 Web Workers 來進行非同步計算。
javascript
// 在 Worker 中計算
import SparkMD5 from "spark-md5";
function calculateHash(file) {
return new Promise((resolve) => {
const spark = new SparkMD5.ArrayBuffer();
const reader = new FileReader();
const blobSlice =
File.prototype.slice ||
File.prototype.mozSlice ||
File.prototype.webkitSlice;
const chunkSize = 2097152; // 2MB
let currentChunk = 0;
const chunks = Math.ceil(file.size / chunkSize);
reader.onload = (e) => {
spark.append(e.target.result);
currentChunk++;
if (currentChunk < chunks) {
loadNext();
} else {
resolve(spark.end());
}
};
function loadNext() {
const start = currentChunk * chunkSize;
const end =
start + chunkSize >= file.size ? file.size : start + chunkSize;
reader.readAsArrayBuffer(blobSlice.call(file, start, end));
}
loadNext();
});
}總結
客戶端預處理的優點顯而易見:
- 節省流量:大幅減少伺服器頻寬支出。
- 減輕伺服器壓力:複雜的壓縮運算分散給每個用戶。
- 極致速度:結合 Hash 計算,達成「瞬間上傳」的神奇體驗。
下一篇,我們將挑戰最複雜的前端邏輯:分片上傳的前端排程。
️ 進階挑戰
如果在前端進行了圖片壓縮,原本檔案中的 Metadata(如拍照地點、快門時間)會消失。這對注重隱私或注重攝影數據的應用來說,分別會有什麼影響?
延伸閱讀與資源
- Web.dev: Browser-side image compression
- Spark-MD5: High performance hashing