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

资讯详情

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

InsForge 存储 API 实战指南:Bucket 对象存储的上传、下载、鉴权与数据库集成完整解析

InsForge 存储 API 实战指南:Bucket 对象存储的上传、下载、鉴权与数据库集成完整解析 InsForge 存储 API 实战指南Bucket 对象存储的上传、下载、鉴权与数据库集成完整解析【免费下载链接】InsForgeThe all-in-one, open-source backend platform for agentic coding. InsForge gives your coding agent database, auth, storage, compute, hosting, and AI gateway to ship full-stack apps end-to-end.项目地址: https://gitcode.com/GitHub_Trending/in/InsForge本文基于仓库中 .archive/docs/deprecated/insforge-storage-api.md 整理扩展并对照当前仓库中 backend/src/api/routes/storage/index.routes.ts、backend/src/services/storage/storage.service.ts 等源码逐一核对为你呈现一份可复制、可运行的 InsForge 对象存储StorageAPI 使用手册。读完本文你将掌握如何通过 MCP 工具与 REST 接口管理 Bucket、如何以 multipart/form-data 上传对象并正确使用返回的绝对 URL、如何利用公开/私有 Bucket 实现免鉴权下载与受控访问、如何把文件与元数据分离存储到数据库以及如何读懂统一错误格式并规避常见踩坑点。InsForge 将存储抽象为“Bucket桶 Object对象”两层模型Bucket 是命名空间与访问控制单元公开/私有Object 是实际的文件二进制与元数据。所有对象操作走 REST APIBucket 管理可借助 MCP 工具二者共享同一套鉴权体系。下面按“API 概览 → Bucket 管理 → 对象操作 → 数据库集成 → 错误处理”的顺序完整展开。1. API 概览Base URL、鉴权模型与核心约定1.1 Base URL 与开发环境存储 API 的默认 Base URL 为http://localhost:7130所有对象接口统一挂在/api/storage前缀下核心路径模式为/api/storage/buckets/:bucketName/objects/:objectKey1.2 鉴权规则一览不同操作对鉴权的要求不同整理如下与 路由源码 中verifyAdmin、verifyUser、conditionalDownloadAuth三个中间件的使用位置一一对应操作鉴权要求说明上传PUT/POSTAuthorization: Bearer token需要登录用户或 API Key走verifyUser删除对象DELETEAuthorization: Bearer token需要登录用户走verifyUser下载对象GET公开 Bucket 免鉴权私有 Bucket 需 token由conditionalDownloadAuth先查询 Bucket 可见性再决定是否跳过鉴权列举/管理 Bucket需要管理员鉴权创建、列举、删除 Bucket 均走verifyAdmin管理存储配置、S3 访问密钥需要管理员鉴权GET/PUT /api/storage/config、/api/storage/s3/*均走verifyAdmin在源码层面API Key 调用者与普通用户走的是不同的数据库连接路径普通 JWT 用户通过withUserContext在storage.objects的行级安全策略RLS约束下读写而 API Key 是机器凭据、不具备用户身份使用后端连接池绕过端用户 RLS执行操作。这一点从 storage.service.ts 中大量if (hasApiKey || ctx?.role project_admin) ... runWithRootAccess(...)分支可以得到印证。文档中提示“API keys are for MCP testingAPI Key 用于 MCP 测试”生产环境推荐使用会话 token。1.3 关键约定响应中的 URL 是绝对 URL直接使用即可这是最容易踩坑的一点存储 API 返回的url字段是完整可用的绝对 URL形如http://localhost:7130/api/storage/buckets/avatars/objects/user123.jpg不要自行拼接 host也不要二次加工该 URL该 URL 可直接用于img src、video src或fetch()请求无论公开还是私有 Bucket只要你有权访问URL 都能直接使用。需要补充的是当前源码在构造对象 URL 时还会追加一个?v版本戳查询参数用于 CDN 缓存失效cache-busting每次上传都会基于对象 etag本地存储退化为uploaded_at毫秒时间戳生成新的版本戳保证覆盖写之后拿到的是新 URL 而非陈旧缓存。参见 storage.service.ts 中的buildObjectUrl实现。2. Bucket 管理MCP 工具与 REST 管理端点Bucket 是整个存储体系的根级容器。原文档推荐使用 MCP 工具完成 Bucket 管理同时 REST 也提供了等价的管理端点需管理员鉴权。2.1 通过 MCP 工具管理 BucketMCP 工具功能关键参数create-bucket创建 BucketbucketName、isPublic默认truelist-buckets列出所有 Bucket—delete-bucket删除 BucketbucketNameisPublic默认值为true这一点与 REST 创建接口的参数校验一致在 packages/shared-schemas/src/storage-api.schema.ts 中createBucketRequestSchema定义为isPublic: z.boolean().default(true)。2.2 通过 REST 端点管理 Bucket管理员方法路径说明POST/api/storage/buckets创建 Bucket请求体{ bucketName: avatars, isPublic: true }成功返回 201GET/api/storage/buckets列出所有 Bucket名称、可见性、创建时间PATCH/api/storage/buckets/:bucketName更新 Bucket 可见性请求体{ isPublic: true }DELETE/api/storage/buckets/:bucketName删除整个 Bucket创建 Bucket 的 curl 示例curl -X POST http://localhost:7130/api/storage/buckets \ -H Authorization: Bearer token \ -H Content-Type: application/json \ -d {bucketName: avatars, isPublic: true}成功响应201{ message: Bucket created successfully, bucketName: avatars, isPublic: true, nextActions: This is a PUBLIC bucket - objects can be accessed without authentication. You can use /api/storage/buckets/:bucketName/objects/:objectKey to upload an object to the bucket, and /api/storage/buckets/:bucketName/objects to list the objects in the bucket. }更新可见性PATCH示例# Mac/Linux curl -X PATCH http://localhost:7130/api/storage/buckets/avatars \ -H Authorization: Bearer token \ -H Content-Type: application/json \ -d {isPublic: true} # Windows PowerShell使用 curl.exe嵌套 JSON 需要不同的引号转义 curl.exe -X PATCH http://localhost:7130/api/storage/buckets/avatars \ -H Authorization: Bearer token \ -H Content-Type: application/json \ -d {\isPublic\: true}响应{ message: Bucket visibility updated, bucket: avatars, isPublic: true, nextActions: Bucket is now PUBLIC - objects can be accessed without authentication. }2.3 Bucket 命名规则与底层创建流程Bucket 名必须是合法标识符。当前源码storage.service.ts 的validateBucketName强制要求匹配正则^[a-zA-Z0-9_-]$即只允许字母、数字、连字符和下划线否则返回 400STORAGE_INVALID_PARAMETER原文档强调“不能以下划线开头”这属于推荐约定当前代码层面的硬性约束是上述字符集校验重复创建同名 Bucket 会返回 409STORAGE_ALREADY_EXISTS。创建流程的底层实现是先调用存储提供者provider创建实际存储空间成功后再向storage.buckets表写入一行记录——先写后端、后写数据库的顺序避免了“数据库有记录但存储后端无实际目录”的孤儿记录导致永久 409 的问题。本地文件系统实现见 backend/src/providers/storage/local.provider.tsS3 实现见 backend/src/providers/storage/s3.provider.ts两者都实现了 backend/src/providers/storage/base.provider.ts 定义的统一StorageProvider接口。3. 对象上传PUT 指定 Key 与 POST 自动生成 Key对象上传统一使用multipart/form-data表单字段名为file。3.1 PUT指定 Key 上传创建或覆盖PUT /api/storage/buckets/:bucketName/objects/:objectKeyconst formData new FormData(); formData.append(file, fileObject);curl 示例# Windows PowerShell: 使用 curl.exe curl -X PUT http://localhost:7130/api/storage/buckets/avatars/objects/user123.jpg \ -H Authorization: Bearer YOUR_SESSION_TOKEN \ -F file/path/to/image.jpg成功响应{ bucket: avatars, key: user123.jpg, size: 15234, mimeType: image/jpeg, uploadedAt: 2025-07-18T04:32:13.801Z, url: http://localhost:7130/api/storage/buckets/avatars/objects/user123.jpg?vabc123... }注意两点实现细节当前路由把对象 Key 用通配符objects/*捕获index.routes.ts因此 Key 中可以包含/天然支持“伪目录”结构的对象键例如users/user123/avatar.jpgPUT 语义为“创建或替换”对已存在的 Key 再次 PUT 会原地覆盖且覆盖时不会改变对象的归属者uploaded_by字段在冲突更新分支中被刻意保留相关逻辑见 storage.service.ts 的putObject方法。3.2 POST服务端自动生成唯一 KeyPOST /api/storage/buckets/:bucketName/objects# Windows PowerShell: 使用 curl.exe curl -X POST http://localhost:7130/api/storage/buckets/posts/objects \ -H Authorization: Bearer YOUR_SESSION_TOKEN \ -F file/path/to/image.jpg成功响应201{ bucket: avatars, key: image-1737546841234-a3f2b1.jpg, size: 15234, mimeType: image/jpeg, uploadedAt: 2025-07-18T04:32:13.801Z, url: http://localhost:7130/api/storage/buckets/avatars/objects/image-1737546841234-a3f2b1.jpg }自动生成的 Key 格式为{净化后的文件名}-{毫秒时间戳}-{6位随机串}{原扩展名}例如image-1737546841234-a3f2b1.jpg由 storage.service.ts 的generateObjectKey生成文件名中非[a-zA-Z0-9-_]字符会被替换为连字符并截断到 32 字符随机串由Math.random().toString(36)派生时间戳保证同一毫秒内的并发上传也不会冲突。3.3 上传的 MIME 安全与大小限制上传链路上内置了两道防护见 index.routes.tsMIME 魔数检测enforceSafeMimeType会读取文件内存缓冲通过魔数magic bytes真实探测文件类型覆盖客户端上报的 mimetype可执行类型HTML、SVG、JS 等会被归一化为application/octet-stream避免存储被用于托管恶意脚本工具实现见 backend/src/utils/mime-guard.ts文件大小上限默认最大 50 MB可通过存储配置调整见下文第 8 节超限返回 413。4. 对象下载与公开/私有访问控制4.1 下载对象GET /api/storage/buckets/:bucketName/objects/:objectKey该接口返回对象的原始字节内容带正确的 Content-Type 头而不是 JSON 包装。核心访问规则公开 Bucket无需任何鉴权即可下载私有 Bucket需要携带Authorization: Bearer token。底层实现中下载路由挂载了conditionalDownloadAuth中间件先查询storage.buckets中该 Bucket 的public字段公开则直接放行否则回落到verifyUser。因此你可以在一个私有 Bucket 和一个公开 Bucket 之间通过 PATCH 动态切换可见性下载行为即时生效。此外当前源码为下载提供了更精细的策略本地存储提供者Local直接以 API 自身 URL 返回文件字节缓冲读取S3 提供者私有 Bucket 走presigned URL 重定向默认有效期 1 小时调用方可传expiresIn自定义服务端钳制在 1 秒7 天之间公开 Bucket 不设置过期同时支持代理流式下载proxy mode通过Range请求头支持 206/416 分段响应媒体文件的拖拽播放也能正常工作——这部分在 index.routes.ts 的streamS3ObjectDownload中实现对于被判定为不安全的 MIME 类型响应会强制附加Content-Disposition: attachment强制下载而非内联渲染并设置X-Content-Type-Options: nosniff。4.2 列举对象GET /api/storage/buckets/:bucketName/objects查询参数参数说明默认值prefix按 Key 前缀过滤无limit单页最大条数100当前源码钳制在 11000offset分页偏移量0search按文件名Key模糊搜索无curl 示例# Windows PowerShell: 使用 curl.exe curl -X GET http://localhost:7130/api/storage/buckets/avatars/objects?limit10prefixusers/ \ -H Authorization: Bearer token原文档记载的响应示例包含分页头X-Total-Count、X-Page、X-Page-Size{ bucketName: avatars, prefix: null, objects: [ { bucket: avatars, key: user123.jpg, size: 15234, mimeType: image/jpeg, uploadedAt: 2025-07-18T04:32:13.801Z, url: http://localhost:7130/api/storage/buckets/avatars/objects/user123.jpg } ], pagination: { limit: 100, offset: 0, total: 1 }, nextActions: You can use PUT /api/storage/buckets/:bucketName/objects/:objectKey to upload with a specific key, or POST /api/storage/buckets/:bucketName/objects to upload with auto-generated key, and GET /api/storage/buckets/:bucketName/objects/:objectKey to download an object. }需要说明对照当前路由实现index.routes.ts现在的响应体已调整为{ data, pagination, nextActions }结构——data承载对象数组、pagination内含offset/limit/total并经过listObjectsResponseSchema校验见 storage-api.schema.ts。无论哪种形态pagination.total都是过滤后的总数可作为前端分页依据对象数组中的url均为可直接使用的绝对 URL。4.3 删除对象删除单个对象DELETE /api/storage/buckets/:bucketName/objects/:objectKey# Windows PowerShell: 使用 curl.exe curl -X DELETE http://localhost:7130/api/storage/buckets/avatars/objects/user123.jpg \ -H Authorization: Bearer token响应{ message: Object deleted successfully }批量删除对象当前源码新增能力DELETE /api/storage/buckets/:bucketName/objects请求体{ keys: [a.txt, b.txt] }最多 1000 个 Key返回逐 Key 的状态结果{ results: [ { key: a.txt, status: deleted }, { key: b.txt, status: notFound } ] }删除的底层顺序是“先删数据库行、后删存储后端”数据库删除成功但存储删除失败时会记录警告日志并返回failed状态便于调用方感知并重试避免产生孤儿数据。5. 与数据库集成文件与元数据分离InsForge 存储与数据库是两个独立模块推荐的集成模式是对象二进制进存储对象元数据进数据库。数据库表中用json列存放对象元数据即可。5.1 Option 1PUT 指定 Key 存储元数据// Step 1: 以已知 Key 上传对象 const formData new FormData(); formData.append(file, file); const upload await fetch(/api/storage/buckets/images/objects/avatar.jpg, { method: PUT, headers: { Authorization: Bearer ${token} }, body: formData }); // Step 2: 将元数据写入数据库 const records [{ userId: user123, image: await upload.json() // 存储对象元数据 }]; await fetch(/api/database/profiles, { method: POST, headers: { Authorization: Bearer ${token}, Content-Type: application/json }, body: JSON.stringify(records) });5.2 Option 2POST 自动生成 Key 存储元数据// Step 1: 以服务端生成的唯一 Key 上传 const formData new FormData(); formData.append(file, file); const upload await fetch(/api/storage/buckets/images/objects, { method: POST, headers: { Authorization: Bearer ${token} }, body: formData }); const fileData await upload.json(); // Step 2: 把含自动生成 Key 的元数据写入数据库 const records [{ userId: user123, image: fileData // 包含自动生成的 key }]; await fetch(/api/database/profiles, { method: POST, headers: { Authorization: Bearer ${token}, Content-Type: application/json }, body: JSON.stringify(records) });两种方式的取舍需要稳定、可预测的对象地址如固定头像路径覆盖即可更新选 PUT不在乎 Key、希望保证唯一性如用户发帖图片选 POST。数据库记录中保存响应返回的key和url字段读取时即可直接拼装img src。从数据流上看上传响应中的size、mimeType、uploadedAt与数据库storage.objects表中的元数据行一一对应服务端通过INSERT ... ON CONFLICT DO UPDATE在写入存储后同步元数据行这正是“数据库只存元数据”落地的证据。6. 错误响应格式与常见错误码所有存储接口的错误响应统一采用以下格式{ error: ERROR_CODE, message: Human-readable error message, statusCode: 400, nextActions: Suggested action to resolve the error }例如 Bucket 不存在{ error: BUCKET_NOT_FOUND, message: Bucket nonexistent does not exist, statusCode: 404, nextActions: Create the bucket first }结合路由源码实际可观测到的常见错误码与触发条件如下error 代码HTTP 状态码典型触发场景STORAGE_ALREADY_EXISTS409重复创建同名 Bucket或对象 Key 与既有记录冲突数据库唯一约束 23505STORAGE_NOT_FOUND404Bucket / 对象不存在STORAGE_INVALID_PARAMETER400Bucket 名或 Key 非法、请求体不符合 schema、expiresIn非数字等STORAGE_PERMISSION_DENIED403RLS 拒绝写入数据库权限错误 42501或缺少用户上下文编写客户端时建议始终以error代码而非 message 文本做分支判断并善用nextActions字段向用户给出可执行的下一步指引。7. 重要规则与最佳实践清单最后汇总原文档的“Important Rules”并结合源码补充实践建议对象操作规范上传一律使用 multipart/form-data字段名固定为file数据库只存元数据不存二进制元数据建议用json列类型响应中的url是绝对 URL直接用于前端展示与 fetch切勿二次拼接 host。Bucket 命名只允许字母、数字、连字符、下划线源码正则^[a-zA-Z0-9_-]$遵循文档约定不以_开头保持命名整洁命名一经创建即被复用创建同名 Bucket 会得到 409。鉴权与工具分工Bucket 管理推荐使用 MCP 工具create-bucket/list-buckets/delete-bucket或使用等价的管理员 REST 端点对象操作统一走 REST API所有写操作都需要鉴权公开 Bucket 的下载免鉴权API Key 适用于 MCP 测试与机器场景生产环境优先使用会话 token。安全与扩展能力源码补充上传内置 MIME 魔数校验可执行类型自动降级为application/octet-stream下载端对不安全 MIME 强制attachment存储配置接口GET/PUT /api/storage/config管理员可动态调整全局最大文件大小maxFileSizeMb允许 1200 MB默认 50 MB参见 storage-config.service.ts如需超大文件直传 S3可使用当前源码提供的上传/下载策略接口POST /api/storage/buckets/:bucketName/upload-strategy、GET /api/storage/buckets/:bucketName/download-strategy/objects/*、POST .../confirm-upload获取 presigned URL 后在客户端直传/直下绕过网关代理面向 S3 生态的S3_BUCKET部署还提供/storage/v1/s3网关与访问密钥管理接口GET /api/storage/s3/config、POST/DELETE /api/storage/s3/access-keys可对接 AWS SDK 与各类 S3 工具。掌握以上内容后你就可以在 InsForge 上完成“上传文件 → 落库元数据 → 公开/私有访问控制 → 前端直接引用绝对 URL”的完整文件管理闭环。如果想进一步深入推荐阅读存储服务的完整实现 backend/src/services/storage/storage.service.ts、提供者抽象 backend/src/providers/storage/base.provider.ts以及单元测试如 storage-routes.test.ts、localstorageprovider.test.ts来验证各接口行为。【免费下载链接】InsForgeThe all-in-one, open-source backend platform for agentic coding. InsForge gives your coding agent database, auth, storage, compute, hosting, and AI gateway to ship full-stack apps end-to-end.项目地址: https://gitcode.com/GitHub_Trending/in/InsForge创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表