在雲原生與海量資料處理領域,上傳數十 GB 甚至數 TB 的大檔案(如 4K 原始影音素材、大模型權重 Checkpoints、資料庫全量備份 dump)是極為常見的業務場景。

如果使用標準的 HTTP PUT Object 單次傳輸方式上傳一個 50 GB 的檔案,只要在傳輸至 99% 時遭遇一次短暫的網路抖動、TCP 逾時或連線中斷,整個連線就會徹底失敗,客戶端必須從 0% 全部重新上傳。這不僅造成網路頻寬的極大浪費,更讓傳輸成功率隨檔案大小呈指數級暴跌。

為了解決大檔案上傳的可靠性與傳輸效率問題,AWS S3 及其相容物件儲存(如 Google Cloud Storage、MinIO、Cloudflare R2)設計了一套高度工程化的架構協議:分段上傳(Multipart Upload,簡稱 MPU)。

本文將從協定規範、客戶端平行分塊上傳管線、斷點續傳狀態機到 S3 內部零拷貝中繼資料合併,深度拆解其全鏈路架構。


S3 分段上傳架構全景圖

以下是從檔案分塊、初始化取得 UploadId、多線程並行傳輸、中斷後續傳探測到 S3 最終合併物件的完整架構拓撲:

S3 大檔案分段上傳 (Multipart Upload) 與斷點續傳架構 展示大檔案動態切片、Initiate 初始化取得 UploadId、平行上傳分片與 ETag 校驗、網路抖動斷點續傳以及 S3 Complete 分散式中繼資料合併全流程。1. 切片與初始化Initiate Upload巨型檔案 (50 GB)• 單次 PUT 失敗率極高• 記憶體壓力與網路抖動• 切割為 500 個 100MB 分片CreateMultipartUpload• POST /bucket/large.bin• S3 分配 UploadId• `VXBsb2FkSW...`• 本機持久化 Upload 狀態• IndexedDB / SQLite 保存分片規格限制 (S3)• 最小分片: 5 MB• 最大分片: 5 GB• 最多允許 10,000 個分片• 單檔案最大支援 5 TB2. 平行傳輸管線Parallel UploadPartWorker 1 ➔ Part #1100MB ➔ ETag: "a1b2c3d4..."✅ 傳輸成功 (HTTP 200)Worker 2 ➔ Part #2100MB ➔ ETag: "e5f6g7h8..."✅ 傳輸成功 (HTTP 200)Worker 3 ➔ Part #3⚡ 網路抖動 / 連線中斷🔄 自動指數退避重試該 PartWorker 4 ➔ Part #4100MB ➔ ETag: "i9j0k1l2..."✅ 傳輸成功 (HTTP 200)頻寬極大化利用多線程並行,榨乾網路鏈路3. 斷點續傳與校驗ListParts & VerificationListParts 斷點探測• 瀏覽器重新整理/斷網重連• 呼叫 ListParts(UploadId)• 取得 S3 已收訖分片清單• 跳過 Part 1, 2, 4• 僅需補傳 Part 3 (極速續傳)分片 MD5 / CRC32 校驗• 客戶端計算 Content-MD5• S3 比對位元一致性• 發現不一致立即拒絕• 杜絕傳輸位元翻轉錯誤生命週期過期清理• AbortIncompleteMultipart• 超過 7 天未完成自動刪除• 防範孤兒分片偷吃費用4. 完成合併與發布CompleteMultipartComplete Request傳送有序 Parts 結構:[1: "a1...", 2: "e5...",3: "c3...", 4: "i9..."]• 驗證所有分片已到位• 觸發物件中繼資料組裝零拷貝中繼資料合併• 不做物理檔案拷貝• S3 指針樹指向各分塊• 毫秒級快速完成裝配• 產生複合 ETag (xxx-4)最終可讀取物件• 完整 50GB Object 上線• 觸發 S3 Event (Lambda)S3 分段上傳流程簡圖切片與 Initiate 初始化 ➔ 平行傳輸 Part #1~#N ➔ 斷點續傳 ListParts 校驗 ➔ Complete 零拷貝中繼資料合併。1. 切片與 Initiate 初始化• 巨型檔案按 5MB~5GB 切割為多個分片• 呼叫 CreateMultipartUpload 獲取 UploadId• 本地 IndexedDB/SQLite 記錄傳輸狀態機• 為多線程並行傳輸做好數據分發2. 平行傳輸與失敗重試• 多個 Worker 同時發起 UploadPart 請求• 每個分片上傳成功後 S3 回傳專屬 ETag• 某個分片遭遇網路中斷只重試該分片• 徹底避免大檔案從 0% 全部重傳的代價• 充分榨乾客戶端上行頻寬3. 斷點續傳與 ListParts• 用戶重整頁面或斷線後再次連線• 查詢 ListParts 獲取 S3 端已存分片清單• 比對本地檔案塊,僅補傳缺失的 Part• Content-MD5 / SHA256 校驗防止位元翻轉• S3 生命週期策略自動清理過期殘留分片4. 零拷貝中繼資料合併• 提交 CompleteMultipartUpload 包含完整清單• S3 底層僅更新 Object Metadata 指針樹• 毫秒級完成邏輯拼接,無需物理拷貝• 生成複合 ETag: hash(md5_1+md5_2...)-N• 完整物件立即可供全球讀取
圖 1:S3 大檔案分段上傳全鏈路架構 — 分塊並行傳輸、斷點續傳狀態機與分散式零拷貝合併

1. 為什麼需要分段上傳?

1.1 單次 PUT 上傳的極限與瓶頸

  1. 單一連線無法榨乾上行頻寬:受限於單一 TCP 連線的滑動視窗(Window Size)與擁塞控制演算法(如 CUBIC/BBR),單線程傳輸無法充分利用千兆/萬兆光纖頻寬。
  2. 記憶體溢出(OOM)風險:伺服器若需在記憶體中緩衝整個物件進行校驗或加密,巨型檔案會直接吃滿 RAM。
  3. 錯誤復原成本極高:50 GB 檔案在 49.9 GB 處失敗,重試代價是再次傳輸完整的 50 GB。

1.2 S3 Multipart Upload 規範限制

AWS S3 對分段上傳制定了精確的邊界規範:

  • 物件大小範圍:5 MB 到 5 TB。
  • 分片大小限制(Part Size):5 MB 到 5 GB(最後一個分片可以小於 5 MB)。
  • 最大分片數量:最多允許 10,000 個分片(Part Numbers: 1 ~ 10,000)。
  • 計算公式:若要上傳 5 TB 檔案,最小分片大小為 5 TB / 10,000 ≈ 524 MB。

2. S3 分段上傳三階段標準協議

分段上傳將一個龐大的傳輸任務解耦為三個標準的 REST API 階段:

S3 大檔案分段上傳(Multipart Upload)三階段標準協議時序 展示客戶端 Client 與 Amazon S3 物件儲存之間:1. Initiate 獲取 UploadId、2. 多線程並行 PUT 分段並返回各 Part ETag、3. Complete 提交已排序分段清單並觸發零拷貝中繼資料合併瞬間上線。Client (客戶端 SDK)Amazon S3 儲存叢集1. POST /bucket/large.iso?uploads ➔ 獲取 UploadId2a. PUT Part 1 (64MB) [並行 Worker 1] ➔ 回傳 ETag_1 ("b10a...")2b. PUT Part 2 (64MB) [並行 Worker 2] ➔ 回傳 ETag_2 ("1b2c...")3. POST /bucket/large.iso?uploadId=xxx (提交有序 Part & ETag 清單)✅ 200 OK ── S3 內部零拷貝中繼資料指針合併,最終物件瞬間發布上線!
STAGE 1: INITIATE

1. 初始化上傳會話

• 客戶端呼叫 POST /bucket/file?uploads。

• S3 返回全域唯一 UploadId,客戶端將其持久化至本地(IndexedDB/SQLite),作為斷點續傳的 Session 錨點。

↓ 並行 Worker 池傳輸
STAGE 2: UPLOAD PARTS

2. 多執行緒並行分段上傳

• 檔案切割為多個 Part(例如每塊 64MB),多線程同時發送 PUT Part N。

• S3 持久化分片後返回 ETag 雜湊簽名。

• 斷點續傳關鍵:任何單一分片失敗僅重試該分片,其餘成功分片完全不受影響!

↓ 收集全部 ETag
STAGE 3: COMPLETE

3. 完成合併發布

• 客戶端提交依照 PartNumber 嚴格升序排序的清單。

• 呼叫 CompleteMultipartUpload 請求。

ZERO-COPY SUCCESS

✅ 零拷貝中繼資料指針組裝

S3 不做繁重的物理硬碟搬移,僅在分散式中繼資料庫建立指針樹,毫秒級瞬間發布完整 Object!

圖二:S3 分段上傳三階段標準協議(Initiate、Upload Part、Complete)時序流程

2.1 階段一:初始化(Initiate Multipart Upload)

客戶端發起 CreateMultipartUpload 請求,S3 在元數據庫中建立該上傳任務的會話記錄,並返回一個全域唯一的字串 UploadId。 在呼叫 Complete 或 Abort 之前,該 UploadId 會一直保持有效。

2.2 階段二:分段並行上傳(Upload Part)

  • 客戶端將檔案按固定大小(如 64 MB)切分為多個 Part。
  • 啟動 Worker 執行緒池,並行發起多個 HTTP PUT 請求,附帶 partNumber 與 uploadId。
  • S3 每接收並持久化一個分片,會在 HTTP Response Header 中返回該分片的 ETag(通常為該分片內容的 MD5 雜湊值)。
  • 斷點續傳關鍵:如果 Part 3 傳輸失敗,客戶端只需針對 Part 3 進行指數退避重試,已經成功上傳的 Part 1、Part 2 完全不受影響!

2.3 階段三:完成組裝(Complete Multipart Upload)

當所有分片上傳完畢,客戶端發送 CompleteMultipartUpload 請求,Body 中包含一個按照 PartNumber 嚴格升序排序的清單:

<CompleteMultipartUpload>
  <Part>
    <PartNumber>1</PartNumber>
    <ETag>"b10a8db164e0754105b7a99be72e3fe5"</ETag>
  </Part>
  <Part>
    <PartNumber>2</PartNumber>
    <ETag>"1b2cf535f27731c974343645a3985328"</ETag>
  </Part>
</CompleteMultipartUpload>

S3 驗證清單無誤後,將分片組裝為最終對外可見的單一 Object,並產生最終的複合 ETag。


3. 客戶端並發分塊與斷點續傳引擎實作

在前端或客戶端 SDK 中,為了實現高可用與斷點續傳,通常會構建一個狀態機管理模組:

// S3 分段平行上傳引擎核心架構
interface PartUploadTask {
  partNumber: number;
  startByte: number;
  endByte: number;
  etag?: string;
  status: "PENDING" | "UPLOADING" | "COMPLETED" | "FAILED";
}

class ResumableS3Uploader {
  private file: File;
  private uploadId: string | null = null;
  private partSize = 10 * 1024 * 1024; // 10 MB per part
  private concurrency = 4; // 同時 4 個並行 Worker

  async start() {
    // 1. 檢查本機 IndexedDB / SQLite 是否有未完成的 uploadId
    this.uploadId = await this.loadSavedUploadId();

    if (!this.uploadId) {
      this.uploadId = await this.initiateS3Upload();
      await this.saveUploadState();
    }

    // 2. 向 S3 查詢已成功接收的分片 (ListParts 探測)
    const uploadedParts = await this.fetchAlreadyUploadedParts(this.uploadId);

    // 3. 建立並過濾出尚未完成的分片任務隊列
    const tasks = this.buildPartTasks(uploadedParts);

    // 4. 透過並行 Worker 池執行上傳
    await this.runWorkerPool(tasks);

    // 5. 提交 Complete 請求完成合併
    await this.completeUpload(tasks);
    await this.clearSavedUploadState();
  }
}

4. 數據完整性防禦與 S3 複合 ETag 計算

在傳輸數十 GB 的資料時,網路設備(路由器、交換機)可能會發生微小的位元翻轉(Bit Flip)。S3 透過多層校驗機制確保絕對的數據完整性:

4.1 Content-MD5 / SHA256 校驗

客戶端在發送每個 UploadPart 請求時,在 Header 中附帶 Content-MD5。S3 伺服器在接收串流的同時動態計算 MD5,若計算值與 Header 不符,立即拒絕並回傳 400 Bad Digest。

4.2 S3 複合 ETag 的生成原理

對於單次 PUT 上傳的物件,其 ETag 就是全檔案的 MD5 雜湊值;但對於 Multipart Upload 生成的物件,S3 的 ETag 採用了特殊的複合計算公式:

ETag = Hex( MD5( Binary(MD5_1) + Binary(MD5_2) + ... + Binary(MD5_N) ) ) + "-" + N

例如 ETag 為 "c8b598...-4",其中後綴 -4 明確標示該物件是由 4 個分段組裝而成。客戶端可以使用相同演算法在本地校驗整個檔案的一致性。


5. S3 伺服器內部合併機制:邏輯中繼資料指針合併

許多工程師誤以為在呼叫 CompleteMultipartUpload 時,S3 伺服器會在磁碟上將 500 個 100MB 的物理檔案讀出並寫入一個新的 50GB 物理檔案中。

如果這樣做,S3 每次 Complete 上傳都要耗費數十秒做磁碟 I/O 拷貝,且會產生嚴重的 I/O 寫入放大!

在現代物件儲存架構(如 S3、MinIO)中,Complete 過程採用了零拷貝中繼資料指針合併(Zero-Copy Metadata Linking):

  1. 每個分段在寫入時,已經以底層區塊或糾刪碼(Erasure Coding)的形式分散儲存在各個 Storage Node 上。
  2. CompleteMultipartUpload 請求只會在分散式元數據資料庫(Metadata Key-Value Store)中更新該 Object 的結構:
    {
      "object_key": "large.iso",
      "total_size": 52428800000,
      "parts": [
        { "part": 1, "size": 104857600, "node_block_ref": "node-101/blk-8812" },
        { "part": 2, "size": 104857600, "node_block_ref": "node-204/blk-9931" }
      ]
    }
  3. 整個 Complete 請求在 幾毫秒內 即可完成,隨後該 Object 立即對全球用戶開放讀取(GET Range 請求會根據 Offset 映射到底層各個 Part 指針)。

6. 生產維運暗坑:未完成分段導致的儲存費用黑洞

在生產環境中,分段上傳有一個致命的維運陷阱:孤兒分片(Incomplete Multipart Uploads)。

  • 若某個客戶端發起了 CreateMultipartUpload,上傳了 40 GB 的分片後意外崩潰且永遠沒有發起 Complete 或 Abort。
  • 這些已經上傳的 40 GB 分片在 S3 Bucket 中對用戶不可見(不會出現在 ListObjects 中),但 AWS S3 會持續針對這些分段收取標準儲存費用!

6.1 解決方案:配置 S3 生命週期策略(Lifecycle Rule)

在每個 S3 Bucket 上,必須強制開啟 AbortIncompleteMultipartUpload 生命週期規則:

{
  "Rules": [
    {
      "ID": "AutoAbortIncompleteMultipartUploads",
      "Status": "Enabled",
      "Filter": {},
      "AbortIncompleteMultipartUpload": {
        "DaysAfterInitiation": 7
      }
    }
  ]
}

設定此規則後,任何超過 7 天未完成組裝的過期分片將由 S3 後台自動刪除,徹底杜絕無效帳單膨脹。


總結

S3 Multipart Upload 透過三階段協定解耦、多線程平行傳輸、粒度化斷點重傳與零拷貝元數據組裝,為巨型大檔案傳輸提供了工業級的高可靠性與效能保障。

掌握分段大小規劃、客戶端並發狀態機設計、複合 ETag 校驗與生命週期自動清理,是構建現代高可用雲端資料傳輸通道的核心必備功底。