Uppy 把 S3 兼容对象存储变成可管理文件库:@uppy/s3 与 @uppy/transloadit-storage 接入实战 Uppy 把 S3 兼容对象存储变成可管理文件库uppy/s3 与 uppy/transloadit-storage 接入实战【免费下载链接】uppyThe next open source file uploader for web browsers :dog:项目地址: https://gitcode.com/gh_mirrors/up/uppyUppy 仓库这次带来两个实验性插件uppy/s3让你在 Uppy 里直接浏览、挑选乃至管理 AWS S3、Cloudflare R2、MinIO 等 S3 兼容对象存储uppy/transloadit-storage把同样的浏览体验指向 Transloadit Storage 工作区。两者都不再是单纯的上传工具而是把桶变成一个可进出的文件库权限与边界则由服务端 Companion 统一把关。先说清楚实验性意味着什么这两个插件、uppy/locales以及依赖它们的 Companion 端点选项与 HTTP API 在 minor 版本里也可能不兼容变更因为最终要让位给一个独立的文件管理器插件。插件安装时会打印 warningCompanion 配置了任一 provider 时启动也会打警告。升级时浏览器插件与 Companion 必须同时升级——旧版 Companion 的列表不返回session字段时界面只显示只读动作旧版浏览器插件不认识新错误码时只能回退展示{ message }已经排进队列的导入也需要重新选择。 先选形态Picker 挑文件Manager 管文件mode选项决定整套交互的形态默认picker列表行是复选框选中即把文件加入 Uppy 上传队列绝不改动任何已有对象。mode: manager则切换成文件库形态——点击打开条目详情、多选藏在显式开关之后并解锁重命名/移动、删除、新建文件夹以及批量移动/批量删除直到专门的 file manager 插件出现之前它是过渡方案。形态之外还有三个选项控制会话行为autoConnect默认true没有已存 Companion 会话时直接登录而不是先渲染连接界面去试探一次注定 401 的会话keepStateOnClose默认false面板关闭后保留当前文件夹与已加载的树管理型 UI 重开面板时回到原处getGrant由你的后端返回一份短期存储授权JWT插件自动用它连接、只读授权时隐藏变更动作、会话过期时静默换新再重试。还有一个容易忽略的细节文件进入队列后可能比产生它的会话活得更久。因此选中文件时下载 URL 会带上选择那一刻的桶?bucket参数Companion 在读任何字节前先校验它——用户之后连到别的会话读不了同名 key对应错误码S3_SELECTED_IN_OTHER_SESSION。完整选项定义见 packages/uppy/s3/src/S3.tsx。 桶的可见范围单租户 bucket 还是多租户 grant插件刻意没有bucket选项。浏览器从不指名桶它只请求一个会话Companion 决定这个会话能看见什么。服务端有两条互斥的路bucket 模式Companion 配置bucket加可选prefix所有能触达 Companion 的人浏览同一个桶。这意味着 Companion 必须部署在你自己的认证之后——它是单租户方案安全边界在你的网络层。grant 模式你的认证后端为用户签发短期存储授权桶、可见前缀、可否写都写在授权里Companion 只负责验签。这是多租户的正解。如何签发多租户存储授权Grant 是 v1 线格式的短期 JWTpayload 固定为{ v: 1, bucket, prefix, scopes, exp, iat?, sub? }验签规则packages/uppy/companion/src/server/provider/s3/grant.tsscopes只能取read/write且必须包含read——不授读权的 grant 不构成会话直接拒绝fail closedgrantSecret只配 HS256grantPublicKey接受 ES/RS/PS 系列非对称公钥此时 Companion 只能验签不能签发none算法永不接受公钥也无法被当作 HMAC 密钥重放两者都接受字符串或数组多密钥并存就是为轮换准备的验证时逐个候选尝试grant 过期会被映射为 HTTP 401浏览器插件据此静默换新 grant 重试多个并发失败共享一次续期而不是把用户踢回连接界面。边界原则要分清provider 凭据本身能触达什么是 IAM/桶策略的职责Companion 强制的是 grant 里的prefix边界并拒绝含\或./..段的 key规避某些 S3 兼容端点和 URL 解析层对点段的越界行为。 Manager 模式内置了哪些文件动作当会话可写grant 带write或 bucket 模式且enableActions ! false时插件提供三组内置动作全部可追加自定义项动作 id位置行为s3:rename条目 ⋯ 菜单输入纯名字原地改名输入含/的路径则相对会话根目录移动s3:delete条目 ⋯ 菜单删单个文件删文件夹需二次确认连内容一起删s3:newFolder工具栏在当前文件夹下新建文件夹输入经 trim 校验s3:bulkMove批量多选条目整体移入目标文件夹s3:bulkDelete批量删除全部选中项含文件夹时明确提示内容一并删除自定义动作通过actions、toolbarActions、bulkActions三个选项追加在内置项之后分别渲染在 ⋯ 菜单、头部工具栏和多选批量区。所有用户可见文案集中在插件的 locale 里这也是uppy/locales随批升级的原因。文件夹移动为什么由客户端逐条执行对象存储里没有真文件夹它只是 key 前缀——移动/删除一个文件夹意味着触碰其下每个对象。uppy/s3把这个过程放在浏览器里做让每个服务端请求保持短小、允许中途取消、失败后停在已知位置。流程packages/uppy/s3/src/moveFolder.ts广度优先遍历源文件夹收集所有子文件夹与文件用 Set 兼作队列防后端列表误报导致重复遍历按父在前的顺序先创建目标文件夹——目标已存在时直接拒绝移动在碰任何文件之前停下以并发 4 逐批移动文件实时回调进度最后按最深优先删除已清空的源文件夹。它不是原子事务中途失败会留下两边各有一部分的状态重跑一次即可收尾。删除文件夹同理——先删文件再自底向上删空文件夹。⚙️ 服务端边界Companion 的 S3 provider 怎么配浏览器只是冰山一角。Provider 配置在providerOptions.s3下它有独立的可选凭据、region、endpoint未设置的部分回落到顶层s3上传块——因此浏览和上传可以用不同账号、不同桶。standalone 模式下对应环境变量是COMPANION_S3_PROVIDER_*如COMPANION_S3_PROVIDER_SECRET、COMPANION_S3_PROVIDER_GRANT_SECRET。配置 schema 有两条硬校验配了 grant 密钥grantSecret/grantPublicKey就不能再设bucket/prefix反之亦然——两者皆无也拒绝启动上传块旧字段如accessKeyId出现在 provider 块里会明确报错提示改用key/secret。所有变更走三个统一端点任何变更都要求会话带write作用域否则报S3_READ_ONLY_SESSIONPOST /:provider/mutate/deletebody{ id }POST /:provider/mutate/movebody{ id, destination }POST /:provider/mutate/create-folderbody{ name, parentId }单文件移动是复制 删源复制带IfNoneMatch: *与源的 ETag 条件删除带IfMatch于是支持条件请求的端点永远不会覆盖已存在的目标、也不会删掉期间被改写的源失败映射为S3_CONFLICT。移动还可重入——若目标已存在且大小与 ETag 等于源说明上次只差删源直接删源收尾。超过 5GB 的对象拒绝单次复制S3_FILE_TOO_LARGE_TO_MOVE需改用支持 multipart copy 的客户端。错误体{ code, message? }是稳定契约不在实验性变更范围内uppy/core把code映射为 locale 文案展示message仅保留给旧版客户端原样阅读。常用错误码错误码语义S3_NOT_FOUND/S3_ALREADY_EXISTS源不存在 / 目标已存在S3_CONFLICT条件请求失败412/409S3_READ_ONLY_SESSION只读会话发起变更S3_FOLDER_NOT_EMPTY/S3_FOLDER_INTO_ITSELF删非空文件夹 / 把文件夹移进自身S3_FILE_TOO_LARGE_TO_MOVE超 5GB 单复制上限S3_OUTSIDE_ALLOWED_FOLDER/S3_INVALID_GRANT越过前缀边界 / grant 无效S3_SELECTED_IN_OTHER_SESSION下载时?bucket与当前会话不一致全部 16 个 provider 错误码见 packages/uppy/companion/src/server/provider/errorCodes.ts。 uppy/transloadit-storage同一套界面指向 Storage 工作区uppy/transloadit-storage是uppy/s3的子类provider 名换为transloadit-storage在全部 S3 选项之上叠加五个选项选项作用getSmartCdnUrl按 key 返回 Smart CDN URL只有设置了它才出现 Copy Smart CDN URL 动作先写剪贴板失败回退弹窗手动复制getDownloadUrl服务端授权的、按版本钉住的原始下载 URLstoreUploads通过同实例上的uppy/transloadit插件把上传存入面板当前打开的文件夹reopenAfterUpload上传无失败后清空文件、重开面板并刷新新文件立即可见onUploadRequest接管工具栏上传文件动作收到当前文件夹的prefix注意getSmartCdnUrl收到的是列表中的 path不是 asset/version ID你的服务端端点必须做有界查询前缀精确匹配、limit 1确认路径真实存在再按返回的 receipt 构造投递 URL绝不能把可变的 key 或其哈希塞进按版本钉住的 Built-in。Companion 侧该 provider 在providerOptions[transloadit-storage]下配置schema 规定只接受 grant 模式——bucket/prefix字段直接报错native Storage takes no bucket: each grant names its Workspace必须提供apiEndpoint与workspaces映射每个 Workspace 一对key/secretproviderOptions: { transloadit-storage: { grantPublicKey: process.env[STORAGE_GRANT_PUBLIC_KEY], apiEndpoint: https://api2.transloadit.com, workspaces: { my-workspace: { key: wKey, secret: wSecret }, }, }, }grant 把 Workspace 名当作bucket指明可见前缀与是否可写凭据由 Companion 持有grant 永不携带凭据。同一对密钥既签 S3 请求也签原生目录调用HMAC-SHA256。关键差异在移动语义该 provider 覆写了move()把{ auth, source, destination }用 Workspace 密钥做 HMAC-SHA256 签名后 POST 到apiEndpoint /dam/entries/move。文件与整个文件夹都走原生目录端点一次移动保留资产身份与已保留引用端点或凭据不可用时动作直接失败绝不回退到 S3 复制/删除那会创造新资产、丢失身份。因此它声明supportsMoveFolder: true列表返回的session里该字段为真浏览器便不再自己逐文件搬文件夹。storeUploads 与碰撞策略storeUploads的本质是替已安装的uppy/transloadit默认transloaditPluginId为Transloadit生成一份assemblyOptions若该插件已自带assemblyOptions插件会直接报错要求用createStoreAssemblyOptions()显式组合流水线。生成的 Assembly 含一个stored步骤robot: /transloadit/store、use: :original、path指向当前打开的文件夹没有打开时规范化为会话根前缀。你的签名端点必须独立授权目标文件夹——浏览器里的文件夹选择不是授权边界。碰撞策略conflictStrategy三态error默认存储上传默认拒绝碰撞。注意批处理语义——一次上传批次共享同一个 Assembly一次碰撞可能让整个 Assembly 失败、每个文件都被报失败且这不是事务失败前已写入的文件可能已存在重试前先检查 Assemblyrename分配一个空闲名字适合不希望普通文件名碰撞中断多文件上传的场景overwrite有意创建新版本。普通非存储上传不受影响。落库存规范化结果别存路径字符串ASSEMBLY_COMPLETED之后从results[producingStep][i]取规范化存储结果store 步骤标注了:original时它报告在results[:original]而非某个叫stored的字段并在服务端校验已完成的 Assembly——不要信任浏览器提供的回执。建议保存workspace、asset_id、version_id、返回的path、size、mime与可用校验和显式碰撞改名可能改变请求的文件名所以以返回值为准。按 Assembly/step/result ID或 asset/version幂等注册别按文件名或 webhook 次数。三条引用各有用途path读该位置当前的资产重命名会让旧路径失效asset_id跟随逻辑资产并选其当前版本asset_id version_id精确钉住保留的字节覆盖写之后也不回退。通用uppy/s3provider 保持 S3 复制/删除语义会创造新资产所以不要把 S3 列表路径当不可变引用——投递时以asset_id为输入、version_id作为v参数。✅ 接入 Checklist服务端配置在 Companion 的providerOptions.s3或providerOptions[transloadit-storage]下二选一给出bucketprefix或 grant 密钥环境变量形如COMPANION_S3_PROVIDER_*多租户授权认证后端签发 v1 grant{ v: 1, bucket, prefix, scopes: [read,write], exp }与 Companion 的grantSecret/grantPublicKey对齐密钥只留服务端浏览器插件安装uppy/s3或uppy/transloadit-storage设置companionUrl按需配置mode、autoConnect、keepStateOnClose、getGrantTransloadit 场景叠加getSmartCdnUrl/getDownloadUrl/storeUploads上传管线使用storeUploads时确保uppy/transloadit已安装且未自带assemblyOptions明确选择conflictStrategy落库ASSEMBLY_COMPLETED后在服务端校验并保存workspace/asset_id/version_id/path等规范化结果投递走按版本钉住的 URL升级时浏览器与 Companion 同时升级。【免费下载链接】uppyThe next open source file uploader for web browsers :dog:项目地址: https://gitcode.com/gh_mirrors/up/uppy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考