尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Supabase Storage 签名可续传上传(Signed Resumable Upload):基于 Uppy + TUS 与 createSignedUploadUrl 的完整实战

Supabase Storage 签名可续传上传(Signed Resumable Upload):基于 Uppy + TUS 与 createSignedUploadUrl 的完整实战 Supabase Storage 签名可续传上传Signed Resumable Upload基于 Uppy TUS 与 createSignedUploadUrl 的完整实战【免费下载链接】supabaseThe Postgres development platform. Supabase gives you a dedicated Postgres database to build your web, mobile, and AI applications.项目地址: https://gitcode.com/GitHub_Trending/supa/supabase导读对于大文件上传网络中断、弱网环境与并发限制都容易导致整包重传。Supabase Storage 内置了基于 TUS 协议的可续传上传能力本仓库examples/storage/resumable-upload-signed-uppy提供了一份完整的参考实现它在浏览器端使用 Uppy 作为上传 UI 与 TUS 客户端通过边缘函数为每一个文件调用createSignedUploadUrl()申请签名令牌并经由x-signature请求头随 TUS 分片请求提交从而在不暴露服务端密钥的前提下完成安全、可续传的大文件上传。读完本文你将掌握这套“按文件签名 Uppy/TUS 续传”方案的前后端完整接线方式、本地运行步骤以及其中每一个关键配置与调用在仓库源码中的真实依据。原文入口README.md本文所有路径均以仓库根目录为基准。一、签名可续传上传要解决什么问题Supabase Storage 实现并对外暴露了 TUS 协议The Upload Server用于可续传上传相关的能力说明与建议可以参阅文档 resumable-uploads.mdx。它推荐在大文件可能超过 6MB、弱网环境、需要上传进度事件的场景下使用可续传上传。在本仓库的examples/storage目录下并存着两个高度相似但鉴权方式截然不同的示例对比维度resumable-upload-signed-uppy本文主角resumable-upload-uppyTUS 端点…/storage/v1/upload/resumable/sign…/storage/v1/upload/resumable携带凭据每个文件独立签发的x-signature令牌固定的authorization: Bearer token凭据来源服务端createSignedUploadUrl()按需签发客户端静态配置如用户会话令牌安全性令牌按文件路径签发、可限时无需暴露长期密钥需要把令牌直接配置在页面里可以看出签名方案的核心价值在于不再需要把服务端或用户的长期凭据写死在页面 / 暴露给最终上传者而是由可信的服务端代码本示例中的 Supabase Edge Function在文件入队时动态签发“仅能写该文件的受限令牌”浏览器端只持有这张短时令牌即可通过 TUS 完成大文件上传。与此对应的实现背景可参考兄弟示例 resumable-upload-uppy/README.md。二、示例文件结构与职责划分整个示例只有一张单页应用和一个小小的边缘函数却能完整覆盖“前端界面 TUS 续传 服务端签名 存储授权”整条链路examples/storage/resumable-upload-signed-uppy/ ├── index.html # 前端单页Uppy Dashboard TUS 上传 └── supabase/ ├── config.toml # 本地项目配置bucket、函数开关 ├── functions/ │ └── create-upload-token/ │ ├── deno.json # 函数 import map │ └── index.ts # 边缘函数签发 x-signature 令牌 └── migrations/ └── 20241128121139_storage_rls.sql # 允许匿名用户写入 uploads bucket 的 RLS 策略各文件的详细职责如下index.html引入 Uppy v3.6.1 的 Dashboard 与 Tus 插件在file-added事件里为每个文件申请令牌并写入该文件的 TUS header。create-upload-token/index.ts接收{ filename }用服务端管理员上下文调用createSignedUploadUrl()把token返回给前端。config.toml本地开发配置声明了 bucketuploads、函数create-upload-token含verify_jwt false。20241128121139_storage_rls.sql一条 RLS 策略放行匿名用户对uploadsbucket 的 INSERT。三、本地运行步骤附完整命令依据 README 的说明本地完整跑通这套签名上传只需三步第 1 步启动本地 Supabase 项目supabase startCLI 会按项目内 config.toml 启动本地 API、Storage、Functions 等组件并在启动结束后在终端输出本地服务的各类密钥。第 2 步配置发布密钥并指向本地服务打开 index.html在文件顶部的常量区把SUPABASE_PUBLISHABLE_KEY替换为supabase start输出的发布密钥anon keyconst SUPABASE_PUBLISHABLE_KEY // your projects publishable (anon) key const PROJECT_URL http://127.0.0.1:54321 const STORAGE_BUCKET uploadsPROJECT_URL固定为本地 CLI 默认端口54321config.toml 中并未修改默认端口。STORAGE_BUCKET对应配置里声明的 bucket 名uploads见下方 config 说明。第 3 步用静态服务器打开页面开始上传README 给出了两种等价方式# python http server python3 -m http.server # npm http-server npx http-server由于 index.html 使用script typemodule从 CDN import Uppy 的 ES Module直接用浏览器打开本地文件可能受 CORS / module 加载限制因此需要一个本地静态服务器来承载页面。四、核心原理一前端如何把一个文件变成“可签名续传上传”4.1 组装 TUS 客户端与上传端点前端的关键在于 Uppy 的 Tus 插件配置见 index.htmlimport { Uppy, Dashboard, Tus } from https://releases.transloadit.com/uppy/v3.6.1/uppy.min.mjs var uppy new Uppy() .use(Dashboard, { inline: true, limit: 10, target: #drag-drop-area, showProgressDetails: true, }) .use(Tus, { endpoint: ${PROJECT_URL}/storage/v1/upload/resumable/sign, headers: { apikey: SUPABASE_PUBLISHABLE_KEY, }, uploadDataDuringCreation: true, chunkSize: 6 * 1024 * 1024, allowedMetaFields: [bucketName, objectName, contentType, cacheControl], onError: function (error) { console.log(Failed because: error) }, })逐个解读这些参数在签名场景下的意义endpoint指向/storage/v1/upload/resumable/sign。注意与普通Bearer 令牌示例 resumable-upload-uppy/index.html 中使用的/storage/v1/upload/resumable相比这里多了/sign后缀这正是签名可续传上传的专用入口。headers.apikey固定带上本地 anon key作为基础请求身份。uploadDataDuringCreation: true在创建 TUS 上传时即携带元数据让服务端在创建阶段就能拿到 bucket / 对象路径做签名校验。chunkSize: 6 * 1024 * 1024TUS 分片大小取 6MB与文档 resumable-uploads.mdx 中的示例保持一致断点续传正是以分片为单位记录进度中断后可只重传未完成分片。allowedMetaFields白名单之外的元数据不会随请求发出。签名上传必须的bucketName / objectName / contentType都在其中cacheControl则用于按文件设置缓存策略。Dashboard 的limit: 10限制并发上传数量showProgressDetails: true在界面上展示分片级进度详情。4.2 file-added 钩子每个文件独立申请签名并注入 x-signature签名方案与“一次性配置一个 Bearer token”最大的不同在于令牌需要逐文件申请、逐文件注入。这体现在 index.html 的file-added事件处理中async function getSignedUploadToken(filename) { const res await fetch(${PROJECT_URL}/functions/v1/create-upload-token, { method: POST, headers: { Content-Type: application/json, apikey: SUPABASE_PUBLISHABLE_KEY }, body: JSON.stringify({ filename }), }) if (!res.ok) throw new Error(Failed to get upload token) const data await res.json() return data.token } uppy.on(file-added, async (file) { const supabaseMetadata { bucketName: STORAGE_BUCKET, objectName: file.name, contentType: file.type, } file.meta { ...file.meta, ...supabaseMetadata } // Important - add signing token header const token await getSignedUploadToken(file.name) uppy.setFileState(file.id, { tus: { headers: { x-signature: token, }, }, }) }) uppy.on(complete, (result) { console.log(Upload complete! Weve uploaded these files:, result.successful) })这里有四个关键动作填充上传元数据把bucketName、objectName、contentType合并进file.meta供 TUS 创建上传时按allowedMetaFields提交。按文件回调签名函数getSignedUploadToken(file.name)以文件名为入参 POST 到本地 Functions 的/functions/v1/create-upload-token。把令牌写进该文件的 TUS header通过uppy.setFileState(file.id, { tus: { headers: { x-signature: token } } })实现。这是本示例最核心的一行代码——它保证了签名令牌只会被用于与其绑定的那一个文件的上传请求而不是全局共享一个令牌。完成回调uppy.on(complete)在全部上传结束后打印result.successful可在此接入服务端回调、二次校验等后续逻辑。代码注释中的// Important - add signing token header也提示了缺少这一步服务端将无法校验签名上传会被拒绝。五、核心原理二边缘函数如何签发令牌5.1 函数入口与鉴权模式签名必须由“持有服务端权限”的代码完成本示例把它放在 Edge Function 中见 create-upload-token/index.tsimport jsr:supabase/functions-js/edge-runtime.d.ts import { withSupabase } from npm:supabase/server^1 // Deploy with verify_jwt false. export default { fetch: withSupabase({ auth: secret }, async (req, ctx) { try { const { filename } await req.json() if (!filename) { return Response.json({ error: Missing filename }, { status: 400 }) } const { data, error } await ctx.supabaseAdmin.storage .from(uploads) .createSignedUploadUrl(filename) if (error) { return Response.json({ error: error.message }, { status: 500 }) } return Response.json({ token: data.token }) } catch (error) { return Response.json({ error: (error as Error).message }, { status: 500 }) } }), }要点拆解withSupabase({ auth: secret }, ...)声明函数内部使用服务端密钥上下文auth: secret因此ctx.supabaseAdmin拥有绕过 RLS 的管理员能力——它需要替“匿名”的浏览器上传者签发令牌。withSupabase与supabaseAdmin均来自npm:supabase/server^1。参数校验req.json()解析出的filename缺失时直接返回400 Missing filename避免对空路径签发令牌。核心调用ctx.supabaseAdmin.storage.from(uploads).createSignedUploadUrl(filename)在 Storage SDK 中以“bucket 对象路径”为输入签发受限上传令牌。仓库中对该 SDK 方法的接口定义可参见规范文件 supabase_js_v2.yml。令牌返回只把data.token回传给前端函数内部持有的密钥永远不会离开服务端。签名与路径强绑定因为令牌是对uploads/{filename}这个对象路径签发的前端在 TUS 元数据里提交的objectName必须与该路径一致本示例中objectName: file.name签名校验才能通过——这也是为什么令牌要逐文件申请。5.2 与官方文档的行为对照在官方文档 resumable-uploads.mdx 的“Presigned uploads”一节中对同一机制有明确描述可续传上传支持通过调用 SDK 的createSignedUploadUrl方法生成限时的共享 URL并把返回的 token 放进x-signature头参与可续传上传。本文示例 index.html 正是该机制的完整落地形态服务端负责签发、浏览器负责把令牌作为x-signature请求头随 TUS 请求发出。六、配套基建config.toml、迁移与 bucket 授权6.1 本地项目配置 config.tomlconfig.toml 做了四项与本示例强相关的声明project_id resumable-upload-uppy [api] # Disable data API since we are not using the PostgREST client in this example. enabled false [storage] 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] # objects_path ./buckets/uploads [functions.create-upload-token] enabled true verify_jwt false import_map ./functions/create-upload-token/deno.json entrypoint ./functions/create-upload-token/index.ts逐项说明project_id resumable-upload-uppy本地项目标识用于在同一机器上区分不同 Supabase 项目。[api] enabled false本示例只走 Storage 与 Functions不依赖 PostgREST 数据 API因此显式关闭可减少本地启动的组件。[storage] file_size_limit 50MiB整个项目所有 bucket 的默认单文件上限为 50MiB。如果想上传更大的文件需要同步调大此处同时把前端 TUS 的chunkSize保持在低于该上限的合理值。[storage.buckets.uploads] public true声明一个名为uploads的公开 bucketpublic只影响对象是否可公开读取不影响写入授权——写入仍由 RLS 策略决定。被注释掉的file_size_limit、allowed_mime_types、objects_path分别用于对单 bucket 覆盖大小限制、限定 MIME 类型如[image/png, image/jpeg]以及映射本地目录预置对象。[functions.create-upload-token]verify_jwt false允许不带用户 JWT 调用该函数。这与源码注释// Deploy with verify_jwt false相互印证——因为本示例走“匿名 按文件签名”而非“登录用户”模式函数依赖自身的auth: secret服务端上下文来代签。import_map/entrypoint分别指向函数目录内的 deno.json 与index.ts。deno.json 目前为空导入映射{imports: {}}函数所需依赖如supabase/server直接以npm:/jsr:前缀在源码中声明。被注释掉的static_files展示了如需随函数托管静态资源时可使用的 glob 配置方式。6.2 RLS 迁移放行匿名写入20241128121139_storage_rls.sql 只有一条策略但它是整条链路“最后一公里”的授权保障CREATE POLICY allow uploads ON storage.objects FOR INSERT TO public WITH CHECK (bucket_id uploads);FOR INSERT TO public允许public含匿名角色向storage.objects插入记录WITH CHECK (bucket_id uploads)插入对象的 bucket 必须被限定为uploads把写入范围收窄到单一 bucket。配合uploadsbucket 声明为public true读公开与上面这条 INSERT 策略匿名可写浏览器端才能仅凭“anon key x-signature 令牌”把分片写入该 bucket。如果去掉该策略即使拿到了签名令牌匿名上传仍会被 RLS 拦截。七、端到端请求链与安全边界综合前文一次成功的签名可续传上传其完整调用链如下用户在 Uppy Dashboard 中添加文件触发file-added事件前端为该文件填充bucketName / objectName / contentType元数据前端 POST{ filename }到边缘函数create-upload-token函数在auth: secret的服务端上下文中调用createSignedUploadUrl(filename)得到受限令牌前端通过uppy.setFileState把令牌写入该文件 TUS 请求的x-signature头Uppy Tus 插件把文件按 6MB 分片 POST 到/storage/v1/upload/resumable/sign服务端校验签名与元数据后进行 TUS 续传写入中断后 Tus 插件自动按 TUS 协议续传剩余分片全部完成后触发uppy.on(complete)。这套设计的几条安全边界值得在生产中沿用最小权限浏览器端只持有“单 bucket、单对象路径”的受限签名令牌而不是长期有效的管理员密钥或用户访问令牌逐文件隔离令牌在file-added内按文件名申请并通过setFileState注入某一文件令牌泄露不会波及其他文件路径签名与该文件对象路径强绑定函数收敛唯一能代签的入口是部署时以verify_jwt false暴露的边缘函数需确保该函数自身逻辑最小化——本示例仅做参数校验与转发不做任何文件内容处理上传终点收紧即使 RLS 放行了匿名 INSERT也通过bucket_id uploads的WITH CHECK把可写入的 bucket 锁定。从源码结构可以推断若要在生产环境使用这套模板通常还应在签名函数中加入用户态校验如改用auth: user鉴权后校验登录用户是否有权上传、对文件名做规范化处理以及把PROJECT_URL由本地http://127.0.0.1:54321替换为线上项目域名大文件场景下建议使用直连存储域名。八、与普通可续传上传示例的取舍仓库中还提供了不签名、直接携带Bearer令牌的版本 resumable-upload-uppy两者仅在“如何表达上传者身份”上分叉携带用户会话令牌Bearer适合“登录用户才能上传”的业务令牌本身即代表用户身份上传与用户账号天然关联实现最简单按文件签发 x-signature适合“把上传能力开放给匿名或第三方、但不想扩散长期密钥”的业务例如分享式网盘、问卷附件、票据系统等缺点是每次文件入队都需要一次额外的函数调用。两个示例共用同一套前端骨架Uppy v3.6.1、Dashboard、Tus 插件、6MB 分片差异仅在 TUS 端点/resumable还是/resumable/sign与令牌注入方式上非常适合对照阅读先读懂 resumable-upload-uppy/index.html 的基线行为再看本文示例中多出的“申请令牌 → 注入 x-signature”两步即可快速迁移出自己的签名上传方案。总结examples/storage/resumable-upload-signed-uppy用“一个 Uppy 单页 一个 30 行边缘函数 一条 RLS 策略”演示了 Supabase Storage 签名可续传上传的最小可用闭环。其设计要点可以概括为一句话上传能力与密钥彻底分离——服务端用createSignedUploadUrl()按对象路径签发受限令牌浏览器仅凭x-signature请求头驱动 TUS 分片续传。无论是做移动端 / Web 端的大文件直传还是构建对匿名用户开放的分享式上传这套模板连同 config.toml、迁移脚本 与 官方文档 都是可以直接对照落地的参考基线。【免费下载链接】supabaseThe Postgres development platform. Supabase gives you a dedicated Postgres database to build your web, mobile, and AI applications.项目地址: https://gitcode.com/GitHub_Trending/supa/supabase创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表