檔案上傳系統設計 (4) —— 效能優化與大檔案處理
在完成基礎與安全性後,我們要面對的是「真實世界的挑戰」。使用者可能會上傳一張 10MB 的 4K 高畫質照片,但你的網頁其實只需要顯示一個 200px 的頭像;或者使用者在火車上傳檔案,網路斷斷續續,導致大檔案傳到一半毀掉。
這一篇,我們來聊聊如何讓檔案上傳系統「既快又穩」。
一、 圖片處理與自動壓縮
使用者不一定懂得縮圖,但後端一定要。如果你的網站直接加載原始圖片,不僅浪費頻寬,載入速度也會慢到氣死使用者。
建議流程:
- 接收原始檔:先安全存入。
- 非同步處理:利用後端套件(如 Node.js 的
Sharp)生成不同尺寸的縮圖(Thumbnail, Medium, Large)。 - 格式優化:將檔案轉換為
WebP或AVIF格式,體積通常比 JPG/PNG 小 30% 以上。
javascript
const sharp = require("sharp");
async function optimizeImage(inputPath, outputPath) {
await sharp(inputPath)
.resize(800) // 寬度固定 800px,高度自動等比例
.webp({ quality: 80 }) // 轉為 WebP 並設定品質
.toFile(outputPath);
}二、 秒傳機制 (Deduplication)
「秒傳」聽起來很神奇,其實原理非常單純:不重複儲存內容相同的檔案。
實作原理:
- 前端在傳送檔案前,先計算檔案的內容 Hash(例如
SHA-256)。 - 後端先檢查資料庫:是否存在相同的 Hash?
- 如果存在,直接在資料庫新增一筆關聯紀錄,指向同一個物理路徑,並回傳「成功」。
TIP
對於使用者來說,他按「上傳」後瞬間就完成了,這就是極致的體驗優化。
三、 大檔案斷點續傳 (Resumable Uploads)
當檔案達到數百 MB 甚至 GB 級別時,一次性上傳的風險極高。
核心策略:分塊上傳 (Chunking)
- 切片:前端利用
Blob.slice()將檔案切成若干個小塊(例如每塊 2MB)。 - 編號與上傳:每一塊帶上
chunkIndex與uploadId依序或併發傳給後端。 - 狀態追蹤:後端接收並存入臨時目錄,並在資料庫/快取紀錄「已接收第幾塊」。
- 合併:當最後一塊傳完後,後端將所有分片「按順序」拼回去,並做最終 Hash 校驗。
優點:
- 斷點續傳:如果傳到第 50 塊斷線,下次只需從第 51 塊開始傳。
- 防止記憶體溢出:即使是超大檔案,後端每次處理的也只是 2MB 的小分片。
總結
高效能的檔案系統具備以下特質:
- 智慧壓縮:不浪費一公克的頻寬。
- 秒傳效應:節省重複的儲存空間。
- 穩定傳輸:面對大檔案與劣質網路環境,依然能靠分塊與斷點續傳保持韌性。
下一篇,我們將邁向本系列的終點站:邁向雲端架構 (S3 / GCS),學習如何擺脫單機伺服器的限制。
️ 進階挑戰
計算大檔案的 Hash 其實很耗 CPU 資源。如果是 10GB 的檔案,前端計算 Hash 可能會讓瀏覽器卡死,你該如何解決這個問題?
延伸閱讀與資源
- Google Developers: Protocol Buffers Overview
- Sharp: High performance Node.js image processing