大型檔案處理 (Handling Large Blobs)
核心問題
大型檔案(影片、圖片)放 Blob Storage(S3),不放 DB —— 100MB BLOB 會讓查詢、備份、replication 全崩。粗略標準:> 10MB 且不需 SQL 查詢 → Blob Storage(S3 提供無限容量、11 個 9 耐久性)。
但 Blob Storage 沒解決資料傳輸問題。標準做法讓 bytes 流過 application server(client → API server → S3),檔案越大越撐不住:
它對傳輸毫無貢獻,只增加延遲和成本。雲端供應商本有全球基礎設施和巨大頻寬,我們卻把有限的 app server 插在中間製造瓶頸。
解法:直傳 + CDN 直download
不讓資料流過你的伺服器,而是給 client 臨時、有範圍限制的憑證,讓它直接和儲存服務互動。Server 角色從「資料傳輸者」變「存取控制者」:驗證 → 產生憑證 → 退場。
你不會陪每個人走到座位。驗證身份、給手環,讓他們自己找位子。系統只驗證一次授權,然後讓 client 直接和儲存服務互動。
Presigned URL(簡單直傳)
Server 不收檔案,只收「請求上傳許可」→ 驗證用戶/配額 → 產生臨時上傳 URL(15 分~1 小時)。
完全在 app 記憶體裡用雲端憑證簽名。儲存服務之後用自己保存的憑證複算雜湊驗證。
限制條件烘焙進簽名(任何人有 URL 都能用,所以產生時就限制):
content-length-range:最小/最大檔案大小(防 10MB 端點被傳 10GB)content-type:頭像端點只接受圖片
簽名驗證者的差異(下載)
| 方式 | 由誰驗證 | 機制 |
|---|---|---|
| S3 Presigned URL | 儲存服務(持有 secret key) | 對稱簽名 |
| CloudFront 簽署 URL | CDN edge(持公鑰,你持私鑰) | 公私鑰密碼學,edge 不需回 origin |
直接從 Blob Storage 下載:簡單便宜,適合不頻繁;CDN:貴但地理分散 + 快取,適合頻繁存取。
大型檔案斷點續傳(Resumable Upload)
5GB 影片在 100Mbps 要 ~7 分鐘,99% 時斷掉就得重來。所有雲端供應商用分塊上傳解決:
| 供應商 | 分塊機制 | 分塊大小 |
|---|---|---|
| AWS S3 | Multipart Upload(每塊獨立 Presigned URL) | 5MB~5GB |
| GCS | Resumable Uploads(單一 session URL + Range header) | 彈性 |
| Azure | Block Blobs | 4MB~100MB |
斷線時 client 不從頭開始:查詢哪些分塊已成功(S3 ListParts)→ 從第 61 塊繼續。進度追蹤隨分塊自動產生。
所有分塊傳完後 client 必須呼叫完成端點(帶分塊編號 + 校驗和),儲存服務才組裝成最終物件。在此之前只有分塊、沒有可存取的完整檔案。未完成的 Multipart Upload 要收費 → 設生命週期規則 24~48 小時自動清理。
狀態同步的挑戰(最容易卡關)
把 server 移出傳輸路徑後,引入分散式狀態管理:metadata 存 DB、檔案存 Blob Storage,是兩個獨立系統、不同時間更新。
最簡單做法是信任 client(上傳完呼叫 API 說「完成了」),但問題多:
| 問題 | 說明 |
|---|---|
| Race condition | DB 顯示 completed 但檔案還沒到 Storage |
| 孤兒檔案 | client 上傳後、通知前崩潰 → Storage 有檔案、DB 無記錄 |
| 惡意 client | 沒上傳就標記完成 |
| 網路失敗 | 完成通知根本沒抵達 |
主機制用 Storage 事件(S3 → SNS/SQS,帶 storage_key)—— 由儲存服務本身確認什麼存在,client 移出信任鏈。
安全網用對帳:定期任務檢查卡在 pending 的檔案,和 Storage 比對。
「以事件為主、對帳撈漏網之魚」→ 不犧牲直傳效能下維持一致性。
常見 Deep Dive
測試你懂分塊上傳。> 10MB 用 Multipart(S3 5MB+/塊)。斷線時查已上傳的分塊(ListParts)→ 從失敗處續傳。client 把 session ID(S3 upload ID)存 localStorage,App 重啟也能續。記得設生命週期政策清理未完成上傳。
最有效手段很簡單:不要讓用戶立刻存取上傳的東西。建處理流水線 → 先進隔離區(quarantine bucket)→ 病毒掃描/內容驗證/大小檢查 → 通過才移到正式 bucket、DB 標記 available。處理延遲本身就抑制濫用(攻擊者無法立刻知道是否成功)。Presigned URL 一定要加檔案大小限制。
Metadata 存主 DB,和檔案分開。產生 Presigned URL 時立刻建 pending 記錄(含預定 storage_key),這樣檔案存在前就有參照。別用 S3 tag 存(最多 10 個、查詢痛苦)。Storage key 用一致模式 uploads/{user_id}/{timestamp}/{uuid},永不讓 client 自己指定 key(覆蓋風險 + 安全問題)。
- CDN:全球快取,雪梨用戶從本地 edge 拿(200ms → 5ms)
- Range Request(
Range: bytes=0-10485759):大檔案斷點續傳,只請求遺失範圍 - 極端情況:平行分塊下載(同時下 4~6 塊,client 重組,提速 3~4×)—— 但大多用戶受限於總頻寬,通常不值得
何時用 / 何時不用
| ✅ 適合(> 10MB) | ❌ 不適合 |
|---|---|
| YouTube 影片上傳、Instagram 相片、Dropbox、Messenger 媒體 | 小檔案(< 10MB JSON / 表單)→ 一般 API 端點 |
| 需同步驗證(CSV 匯入要先驗 header)→ 讓資料流過 server | |
| 合規要求(金融掃信用卡號、HIPAA)→ 流過認證系統 | |
| 需即時回饋(上傳大頭照馬上要人臉偵測結果)→ async 本質會破壞體驗 |
從「讓 bytes 流過你的基礎設施」轉移到「編排存取權限 + 管理分散式狀態」。狀態同步是許多面試者卡關的地方。
Related Notes
- 07-Caching-Storage/01-Caching — CDN、edge caching 概念
- 10-Design-Patterns/03-Long-Running-Tasks — 上傳事件觸發 async worker(轉碼、縮圖)
- 01-Networking/06-Load-Balancing — CDN、地理分散
- 01-Networking/03-HTTP-and-HTTPS — HTTP PUT、Range Request、簽名
- 10-Design-Patterns/Practice-Design-Patterns