現(xiàn) TUS 協(xié)議的可恢復(fù)上傳實(shí)戰(zhàn)指南)
使用 Supabase Storage 與 Uppy 實(shí)現(xiàn) TUS 協(xié)議的可恢復(fù)上傳實(shí)戰(zhàn)指南【免費(fèi)下載鏈接】supabaseThe Postgres development platform. Supabase gives you a dedicated Postgres database to build your web, mobile, and AI applications.項(xiàng)目地址: https://gitcode.com/GitHub_Trending/supa/supabase導(dǎo)讀本文圍繞 examples/storage/resumable-upload-uppy 這份官方示例展開(kāi)講解如何用 Uppy瀏覽器端上傳組件庫(kù)通過(guò) TUS 協(xié)議向 Supabase Storage 進(jìn)行可恢復(fù)斷點(diǎn)續(xù)傳上傳。讀完后你將掌握為什么要用可恢復(fù)上傳、如何在控制臺(tái)或 SQL 中準(zhǔn)備存儲(chǔ)桶與公網(wǎng)寫(xiě)入策略、如何用四個(gè)核心變量配置前端、以及 TUS 分塊上傳背后的斷點(diǎn)續(xù)傳與并發(fā)沖突機(jī)制。倉(cāng)庫(kù)中還包含配套的 resumable-upload-signed-uppy簽名 URL 變體與官方文檔 apps/docs/content/guides/storage/uploads/resumable-uploads.mdx可作為對(duì)照與深化閱讀。為什么選擇可恢復(fù)上傳在動(dòng)手前先判斷你的場(chǎng)景是否真的需要可恢復(fù)上傳。根據(jù) Supabase 官方文檔 apps/docs/content/guides/storage/uploads/resumable-uploads.mdx當(dāng)滿足以下任一條件時(shí)推薦使用 resumable upload 方案上傳大文件文件體積可能超過(guò) 6MB網(wǎng)絡(luò)不穩(wěn)定移動(dòng)網(wǎng)絡(luò)、弱網(wǎng)環(huán)境下連接可能隨時(shí)中斷需要進(jìn)度事件希望向用戶展示真實(shí)的上傳進(jìn)度條。其底層原理是 Supabase Storage 實(shí)現(xiàn)了 TUSThe Upload Server開(kāi)放協(xié)議。該協(xié)議的核心價(jià)值在于上傳被打斷后可以從上次中斷的字節(jié)位置繼續(xù)而不是從頭再來(lái)??蛻舳藗?cè)既可以使用tus-js-client這樣的底層庫(kù)也可以使用像 Uppy 這類內(nèi)置 TUS 支持的上層組件庫(kù)——本示例正是官方推薦的 Uppy 路線。示例目錄結(jié)構(gòu)總覽倉(cāng)庫(kù)中的 examples/storage/resumable-upload-uppy 目錄非常精簡(jiǎn)只有以下文件examples/storage/resumable-upload-uppy/ ├── README.md # 運(yùn)行說(shuō)明 ├── index.html # 純前端上傳頁(yè)面Uppy Dashboard Tus 插件 ├── supabase/ │ ├── config.toml # 本地/遠(yuǎn)端項(xiàng)目存儲(chǔ)配置 │ └── migrations/ │ └── 20241128121139_storage_rls.sql # 允許公網(wǎng)寫(xiě)入的 RLS 策略 └── supabase-logo-wordmark--dark.png # 頁(yè)面 Logoindex.html是一個(gè)零構(gòu)建、單文件的瀏覽器頁(yè)面通過(guò) CDN 引入 Uppy v3.6.1 的樣式與 ES Module無(wú)需npm install即可演示完整的 TUS 上傳閉環(huán)——這大大降低了復(fù)現(xiàn)成本。前提準(zhǔn)備創(chuàng)建存儲(chǔ)桶與放行策略示例假設(shè)使用 Supabase 控制臺(tái)或 SQL完成兩步前置工作創(chuàng)建存儲(chǔ)桶從 Supabase 控制臺(tái)的 Storage 頁(yè)面新建一個(gè) bucket示例中默認(rèn)叫uploads。添加允許上傳的策略為storage.objects表放行公網(wǎng)INSERT官方給出 SQL 如下CREATE POLICY allow uploads ON storage.objects FOR INSERT TO public WITH CHECK (bucket_id your-bucket-name);示例倉(cāng)庫(kù)中的遷移文件 examples/storage/resumable-upload-uppy/supabase/migrations/20241128121139_storage_rls.sql 就是這條策略的落地版本bucket 名為uploadsCREATE POLICY allow uploads ON storage.objects FOR INSERT TO public WITH CHECK (bucket_id uploads);配套的 supabase/config.toml 則給出了等價(jià)的項(xiàng)目級(jí)配置若用 CLI 管理項(xiàng)目可直接對(duì)照project_id resumable-upload-uppy [api] # Disable data API since we are not using the PostgREST client in this example. enabled false [storage] # The maximum file size allowed for all buckets in the project. file_size_limit 50MiB [storage.image_transformation] enabled false [storage.buckets.uploads] public true # file_size_limit 50MiB # allowed_mime_types [image/png, image/jpeg] # Uncomment to specify a local directory to upload objects to the bucket. # objects_path ./buckets/uploads其中值得注意的參數(shù)含義[api].enabled false本示例不用 PostgREST 數(shù)據(jù)客戶端因此關(guān)閉數(shù)據(jù) API[storage].file_size_limit 50MiB項(xiàng)目級(jí)最大上傳體積上限此處為 50MiB該上限作用于項(xiàng)目?jī)?nèi)所有 bucket[storage.buckets.uploads].public true將uploads桶設(shè)為公開(kāi)注釋掉的allowed_mime_types、objects_path表明你還可以按桶限定 MIME 類型或?qū)⑸蟼鲗?duì)象落到本地目錄以便調(diào)試。注意FOR INSERT TO public意味著任何人都可向該桶寫(xiě)入對(duì)象。示例僅用于演示生產(chǎn)環(huán)境請(qǐng)務(wù)必改用更嚴(yán)格的認(rèn)證如FOR INSERT TO authenticated WITH CHECK (bucket_id uploads AND auth.uid() owner_id)或參考本文第五節(jié)的簽名 URL 方案。配置四個(gè)核心變量打開(kāi) index.html將文件頂部的四個(gè)常量替換為你自己的值const SUPABASE_PUBLISHABLE_KEY replace-with-your-publishable-key const SUPABASE_PROJECT_ID replace-with-your-project-id const STORAGE_BUCKET replace-with-your-bucket-id const BEARER_TOKENreplace-with-your-bearer-token各變量含義如下變量含義獲取方式SUPABASE_PUBLISHABLE_KEY項(xiàng)目的可發(fā)布密鑰anon/publishable keySupabase 項(xiàng)目 Dashboard → Settings → API KeysSUPABASE_PROJECT_ID項(xiàng)目 refURL 中的子域項(xiàng)目 Settings → General形如abcdxyzSTORAGE_BUCKET存儲(chǔ)桶名即上文新建的 bucket 名如uploadsBEARER_TOKEN上傳鑒權(quán)令牌登錄用戶會(huì)話的access_token或服務(wù)角色密鑰演示用第 53 行據(jù)此拼接出 TUS 上傳端點(diǎn)const supabaseStorageURL https://${SUPABASE_PROJECT_ID}.supabase.co/storage/v1/upload/resumable進(jìn)階推薦使用直連 Storage 域名官方文檔特別提示上傳大文件時(shí)應(yīng)優(yōu)先使用直連存儲(chǔ)主機(jī)名以享受多項(xiàng)性能優(yōu)化。即把https://project-id.supabase.co換成https://project-id.storage.supabase.co。對(duì)應(yīng)地TUS 端點(diǎn)應(yīng)寫(xiě)為const supabaseStorageURL https://${SUPABASE_PROJECT_ID}.storage.supabase.co/storage/v1/upload/resumable從源碼讀懂 Uppy 的配置整個(gè)上傳邏輯都封裝在 index.html 的一個(gè)script typemodule中。我們先實(shí)例化 Uppy 并掛載 Dashboard 組件第 55–61 行var uppy new Uppy() .use(Dashboard, { inline: true, limit: 10, target: #drag-drop-area, showProgressDetails: true, })參數(shù)作用inline: true以內(nèi)嵌方式渲染在頁(yè)面#drag-drop-area元素中而不是彈出彈窗l(fā)imit: 10同時(shí)進(jìn)行的上傳任務(wù)并發(fā)上限showProgressDetails: true在界面上展示進(jìn)度明細(xì)。接著掛載 TUS 插件第 62–74 行這是與 Supabase 對(duì)接的關(guān)鍵.use(Tus, { endpoint: supabaseStorageURL, headers: { authorization: Bearer ${BEARER_TOKEN}, apikey: SUPABASE_PUBLISHABLE_KEY, }, uploadDataDuringCreation: true, chunkSize: 6 * 1024 * 1024, allowedMetaFields: [bucketName, objectName, contentType, cacheControl], onError: function (error) { console.log(Failed because: error) }, })逐項(xiàng)拆解其設(shè)計(jì)意圖endpoint即上文的/storage/v1/upload/resumable地址所有 TUS 請(qǐng)求創(chuàng)建/上傳/續(xù)傳都發(fā)往此處headers.authorizationBearer token攜帶訪問(wèn)令牌讓存儲(chǔ)服務(wù)端校驗(yàn)身份headers.apikey攜帶項(xiàng)目的 publishable keyanonymity key這是所有對(duì) Supabase 網(wǎng)關(guān)請(qǐng)求的標(biāo)準(zhǔn)頭uploadDataDuringCreation: true在創(chuàng)建上傳Creation請(qǐng)求時(shí)就攜帶首塊數(shù)據(jù)減少一次 HTTP 往返顯著加快小文件與首塊上傳chunkSize: 6 * 1024 * 1024分塊大小固定為6MB。這是當(dāng)前 TUS 客戶端與 Supabase Storage 約定必須使用的值官方在 resumable-uploads.mdx 中明確注釋“NOTE: it must be set to 6MB (for now) do not change it”allowedMetaFields聲明允許隨 TUS 上傳附帶的自定義元數(shù)據(jù)鍵服務(wù)端據(jù)此從元數(shù)據(jù)中解析對(duì)象信息onError上傳失敗時(shí)的回調(diào)便于定位問(wèn)題。在 file-added 階段注入 Supabase 元數(shù)據(jù)TUS 協(xié)議的上傳創(chuàng)建請(qǐng)求需要把“存到哪個(gè)桶、對(duì)象叫什么、MIME 類型是什么”等信息作為元數(shù)據(jù)傳給服務(wù)端。示例在第 76–89 行用file-added事件完成注入uppy.on(file-added, (file) { const supabaseMetadata { bucketName: STORAGE_BUCKET, objectName: folder ? ${folder}/${file.name} : file.name, contentType: file.type, } file.meta { ...file.meta, ...supabaseMetadata, } console.log(file added, file) })注意第 52 行的const folder 若想在桶內(nèi)建目錄可填入子目錄前綴例如folder documents此時(shí)對(duì)象路徑變?yōu)閐ocuments/文件名。如果對(duì)照官方文檔 apps/docs/content/guides/storage/uploads/resumable-uploads.mdx 中的tus-js-client示例會(huì)發(fā)現(xiàn)還有兩個(gè)可選元數(shù)據(jù)值得了解cacheControl如3600對(duì)象緩存控制metadata可傳JSON.stringify(...)形式的自定義元數(shù)據(jù)寫(xiě)入對(duì)象的user_metadata字段——注意在 Uppy 路線中需要把它加入allowedMetaFields數(shù)組才會(huì)被透?jìng)鳌1O(jiān)聽(tīng)上傳完成事件最后監(jiān)聽(tīng)complete事件在全部上傳成功后做收尾處理第 91–93 行uppy.on(complete, (result) { console.log(Upload complete! Weve uploaded these files:, result.successful) })result.successful數(shù)組中保存本次所有成功上傳的文件信息可用它刷新文件列表或跳轉(zhuǎn)詳情頁(yè)。本地啟動(dòng)與驗(yàn)證由于頁(yè)面完全基于瀏覽器原生 ES Module 與 CDN 依賴只需一個(gè)靜態(tài)文件服務(wù)器即可運(yùn)行。README 推薦用 Python 內(nèi)置服務(wù)器python3 -m http.server在瀏覽器打開(kāi)http://localhost:8000把文件拖入 Dashboard 區(qū)域即開(kāi)始上傳。頁(yè)面中還提供了一條指向官方 Resumable Uploads 文檔的鏈接第 36–38 行。與簽名 URL 變體的區(qū)別若想避免把 bucket 開(kāi)放給匿名用戶可參考同目錄的姊妹示例 examples/storage/resumable-upload-signed-uppy/README.md。它的做法是每個(gè)文件在file-added時(shí)調(diào)用createSignedUploadUrl()換取一次性令牌把令牌放入x-signature請(qǐng)求頭完成鑒權(quán)從而實(shí)現(xiàn)無(wú) RLS 放行策略的受限上傳。底層機(jī)制URL 時(shí)效、并發(fā)與覆蓋理解以下三條由服務(wù)端實(shí)現(xiàn)保證的語(yǔ)義有助于把示例改造成生產(chǎn)級(jí)代碼依據(jù)均為官方文檔對(duì)存儲(chǔ)服務(wù)的描述每個(gè)上傳擁有獨(dú)立的臨時(shí) URL服務(wù)端會(huì)為每次上傳創(chuàng)建獨(dú)立的唯一 URL即使多次上傳到同一路徑也是如此所有分塊通過(guò)PATCH方法發(fā)送到該 URL。這個(gè) URL最長(zhǎng)有效 24 小時(shí)超時(shí)即失效、需要重新發(fā)起上傳——TUS 客戶端庫(kù)含 Uppy通常在 URL 過(guò)期后自動(dòng)創(chuàng)建新 URL 繼續(xù)任務(wù)。并發(fā)沖突返回 409兩個(gè)及以上客戶端同時(shí)向同一個(gè)上傳 URL 推數(shù)據(jù)時(shí)只有一個(gè)能成功其余收到409 Conflict這避免了數(shù)據(jù)損壞兩個(gè)客戶端用不同 URL 上傳同一路徑時(shí)先完成者勝出后者同樣收到409 Conflict只有顯式設(shè)置x-upsert: true請(qǐng)求頭時(shí)才改為“后完成者覆蓋前者”。覆蓋寫(xiě)入的默認(rèn)行為不設(shè)置x-upsert而上傳到一個(gè)已存在的路徑默認(rèn)返回400 Asset Already Exists。官方建議盡量避免覆蓋寫(xiě)CDN 需要時(shí)間把變更傳播到各邊緣節(jié)點(diǎn)期間會(huì)提供過(guò)期內(nèi)容更推薦寫(xiě)入新路徑。如需在 TUS 元數(shù)據(jù)或請(qǐng)求頭中啟用 upsert可參照官方文檔在headers中加入x-upsert: true。生產(chǎn)化要點(diǎn)小結(jié)從這一單文件示例出發(fā)落地到真實(shí)業(yè)務(wù)時(shí)建議鑒權(quán)收斂將演示用的公開(kāi)寫(xiě)入策略替換為authenticated限定或采用簽名 URL 方案域名直連大文件上傳使用https://project-ref.storage.supabase.co直連域名提升性能善用斷點(diǎn)續(xù)傳能力Uppy 基于file.id指紋在刷新/斷網(wǎng)后繼續(xù)未完成任務(wù)服務(wù)端保留上傳會(huì)話直至 24 小時(shí)到期控制分塊大小TUS 客戶端側(cè)chunkSize保持 6MB 約定值后續(xù)版本變化以官方文檔為準(zhǔn)。需要更系統(tǒng)的協(xié)議細(xì)節(jié)上傳 URL 語(yǔ)義、并發(fā)規(guī)則、x-upsert、簽名上傳可繼續(xù)閱讀倉(cāng)庫(kù)內(nèi)的官方指南 apps/docs/content/guides/storage/uploads/resumable-uploads.mdx?!久赓M(fèi)下載鏈接】supabaseThe Postgres development platform. Supabase gives you a dedicated Postgres database to build your web, mobile, and AI applications.項(xiàng)目地址: https://gitcode.com/GitHub_Trending/supa/supabase創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考