Skip to Content
核心概念附件與 Blob

附件 Blob 與 multipart 生命週期

Attachment cell 不直接接收檔案 bytes。前端先完成 resumable multipart upload,拿到 blob.id,再把這個 ID 寫進資料列的 attachment 欄位;下載時則用表、列與 blob 三者重新授權後,在名為 signed_url 的舊欄位取得儲存的存取網址。

路徑是 id-less scope mount

附件 router 分別掛在三個 scope name 下,但路徑不帶 scope ID:

/private/module/custom_tables/chatroom/tables/{table_id}/blobs/... /private/module/custom_tables/department/tables/{table_id}/blobs/... /private/module/custom_tables/company/tables/{table_id}/blobs/...

即使是聊天室/部門表,也不要插入 chatroom_iddepartment_id。仍須選對表的擁有 scope;伺服器從 table_id 驗證 tenant 與有效權限。Blob 路由不接受 acting_chatroom_id:全部 15 個操作——五條路由乘以三個掛載點——已於 2026-07-29 移除該參數,包含 company 掛載點,其 422 acting_chatroom_id is not applicable to a company-scoped table 現在不可能被觸發。受治理的部門表,其 channel floor 是從呼叫者自己被授權的房間解析出來的,見 union 契約

生命週期總覽

init ─→ upload chunk 0..N-1 ─→ status ─→ complete ─→ blob.id 寫入 record └──────────────────────────→ abort record + blob.id ─→ download endpoint ─→ signed_url ─→ 下載 bytes

1. Init

POST .../tables/{table_id}/blobs/multipart/init 使用 multipart/form-data,欄位是 filenamefile_sizecontent_type 與可選 chunk_size。它需要表上的 insert 或 edit 任一寫入能力。

curl -X POST "$BASE/private/module/custom_tables/chatroom/tables/22222222-2222-4222-8222-222222222222/blobs/multipart/init" \ -H "Authorization: Bearer $TOKEN" \ -F "filename=order-note.txt" \ -F "file_size=18" \ -F "content_type=text/plain"

回應提供 opaque session_idchunk_sizetotal_chunks 與約 24 小時後的 expires_at。Init 會先做全域大小、table-level 大小與 MIME 檢查:大小採已宣告上限中的最大值,MIME 採已宣告 allowed_mime_types 清單的聯集;若任一 attachment 欄在該維度不設限,init 也不會在該維度提早拒絕。把 blob ID 寫入特定 cell 時,仍會以目標欄的數量、MIME 與大小上限做權威驗證。

2. Upload chunks 與 status

每一塊都送到共用 public leg:

POST /public/module/etl/multipart/chunk

它沒有 user-auth dependency,因此持有 session_id 就是能替該 session 新增 parts 的 capability,不只是搭配 bearer token 的一般識別值。不要寫入 log、分析事件或可分享 URL。multipart/form-data 欄位為:必填 session_id、必填 zero-based 整數 chunk_index、必填 binary file field chunk,以及選填 chunk_hash(原始 chunk bytes 的小寫 SHA-256)。每個非最後 part 必須剛好是 chunk_size bytes;最後 part 必須剛好是 file_size - (chunk_index * chunk_size) bytes。

成功回傳 session_id、接受的 chunk_index、zero-based uploaded_chunkstotal_chunksprogress_percentis_complete。Index/exact size/hash 無效是 400;session 不存在或過期是 404;part 已上傳或衝突是 409;storage 拒絕大小是 413;cache/storage 失敗或 session reset 是 503(另可能有 storage-policy 403)。這些 public chunk errors 與 private status、complete、abort 對目前使用者的 table/session authorization 是兩個不同邊界。

斷線後呼叫 GET .../multipart/status?session_id=...,比較 uploaded_chunksmissing_chunks,只重送缺少的 zero-based indexes。Status 與後續操作會驗證 session 同時屬於目前使用者與目前表,不能拿另一張表或 ETL 的 session 混用。

3. Complete 或 abort

所有 chunks 上傳完成後,以 multipart/form-datasession_id 呼叫 POST .../multipart/complete。成功回應刻意遮蔽儲存 URL,只回 blob metadata:

{ "blob": { "id": "44444444-4444-4444-8444-444444444444", "filename": "order-note.txt", "content_type": "text/plain", "url": null, "size_bytes": 18 } }

Complete 只建立 blob,不會自動附加到任何資料列。接著把 ID list 寫入 attachment 欄:

{ "附件": ["44444444-4444-4444-8444-444444444444"] }

若使用者取消,上傳尚未 complete 時可呼叫 DELETE .../multipart/abort?session_id=...。Abort 是 terminal;同一 session 不能再 complete,請重新 init。Complete 也會結束該 session。

complete 遇到鎖衝突現在回可重試的 409,而且會保住你的上傳。以前寫入 blob 資料列時遇到暫時性資料庫衝突會走進補償路徑——已完整上傳的物件被刪掉、session 被銷毀、呼叫端拿到 500,只能整個檔案重傳。現在會先辨識衝突:物件與 session 都保留(session 維持 24 小時 TTL),回應是併發合約裡標準的可重試 409。拿同一個 session_id 重呼 complete 就能收尾。complete 對自己的重試也具冪等性:若儲存層在上一次嘗試已經組裝好物件,重試會驗證後直接回傳 blob,而不是拿著已被消耗的 upload id 失敗。另外,完成的上傳現在會記錄實際組裝出來的 size_bytes,不再輕信 init 時客戶端宣告的大小。

4. Download

不要保存 complete 前的 storage URL,也不要直接從 blob metadata 拼 URL。每次下載都呼叫:

GET .../tables/{table_id}/records/{record_id}/attachments/{blob_id}/download

伺服器會確認呼叫者能讀該列、attachment 欄沒有被隱藏、blob 確實出現在該列、且 tenant 相符;無法看見的情況以一致的 404/403 契約避免洩漏。雖然舊欄位名叫 signed_url,這條 route 實際上原樣回傳儲存的 blob.url,不會在此進行密碼學簽章或加上時效限制。expiration_seconds0expires_at 只是建構回應的時間,不能當成可用的到期訊號。請把網址視為敏感資訊,不要保存或分享;每次使用者主動下載時重新請求,也不要假設後續權限變更會撤銷已回傳的網址。

可靠上傳的前端狀態

至少保存 session_idchunk_sizetotal_chunksexpires_at 與本地檔案 fingerprint。重新整理頁面後先查 status;session 過期或 abort 才重新 init。Complete 成功但 record write 失敗時,保留 blob.id 並重試資料列寫入,不要重傳整個檔案。

完整 endpoint 錯誤見附件參考,attachment cell 限制見附件欄位,可操作流程見附件指南API Playground

Last updated on