news 2026/9/15 15:39:08

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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 对象存储(Storage)API 使用手册。读完本文,你将掌握:如何通过 MCP 工具与 REST 接口管理 Bucket、如何以 multipart/form-data 上传对象并正确使用返回的绝对 URL、如何利用公开/私有 Bucket 实现免鉴权下载与受控访问、如何把文件与元数据分离存储到数据库,以及如何读懂统一错误格式并规避常见踩坑点。

InsForge 将存储抽象为“Bucket(桶)+ Object(对象)”两层模型:Bucket 是命名空间与访问控制单元(公开/私有),Object 是实际的文件二进制与元数据。所有对象操作走 REST API,Bucket 管理可借助 MCP 工具,二者共享同一套鉴权体系。下面按“API 概览 → Bucket 管理 → 对象操作 → 数据库集成 → 错误处理”的顺序完整展开。

1. API 概览:Base URL、鉴权模型与核心约定

1.1 Base URL 与开发环境

存储 API 的默认 Base URL 为:

http://localhost:7130

所有对象接口统一挂在/api/storage前缀下,核心路径模式为:

/api/storage/buckets/:bucketName/objects/:objectKey

1.2 鉴权规则一览

不同操作对鉴权的要求不同,整理如下(与 路由源码 中verifyAdminverifyUserconditionalDownloadAuth三个中间件的使用位置一一对应):

操作鉴权要求说明
上传(PUT/POST)Authorization: Bearer <token>需要登录用户或 API Key,走verifyUser
删除对象(DELETE)Authorization: Bearer <token>需要登录用户,走verifyUser
下载对象(GET)公开 Bucket 免鉴权;私有 Bucket 需 tokenconditionalDownloadAuth先查询 Bucket 可见性再决定是否跳过鉴权
列举/管理 Bucket需要管理员鉴权创建、列举、删除 Bucket 均走verifyAdmin
管理存储配置、S3 访问密钥需要管理员鉴权GET/PUT /api/storage/config/api/storage/s3/*均走verifyAdmin

在源码层面,API Key 调用者与普通用户走的是不同的数据库连接路径:普通 JWT 用户通过withUserContextstorage.objects的行级安全策略(RLS)约束下读写,而 API Key 是机器凭据、不具备用户身份,使用后端连接池(绕过端用户 RLS)执行操作。这一点从 storage.service.ts 中大量if (hasApiKey || ctx?.role === 'project_admin') ... runWithRootAccess(...)分支可以得到印证。文档中提示“API keys are for MCP testing(API 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 工具管理 Bucket

MCP 工具功能关键参数
create-bucket创建 BucketbucketNameisPublic(默认true
list-buckets列出所有 Bucket
delete-bucket删除 BucketbucketName

isPublic默认值为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 },成功返回 201
GET/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_-]+$,即只允许字母、数字、连字符和下划线,否则返回 400(STORAGE_INVALID_PARAMETER);
  • 原文档强调“不能以下划线开头”,这属于推荐约定,当前代码层面的硬性约束是上述字符集校验;
  • 重复创建同名 Bucket 会返回 409(STORAGE_ALREADY_EXISTS)。

创建流程的底层实现是先调用存储提供者(provider)创建实际存储空间,成功后再向storage.buckets表写入一行记录——先写后端、后写数据库的顺序避免了“数据库有记录但存储后端无实际目录”的孤儿记录导致永久 409 的问题。本地文件系统实现见 backend/src/providers/storage/local.provider.ts,S3 实现见 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/:objectKey
const 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?v=abc123..." }

注意两点实现细节:

  1. 当前路由把对象 Key 用通配符objects/*捕获(index.routes.ts),因此 Key 中可以包含/,天然支持“伪目录”结构的对象键,例如users/user123/avatar.jpg
  2. PUT 语义为“创建或替换”:对已存在的 Key 再次 PUT 会原地覆盖,且覆盖时不会改变对象的归属者(uploaded_by字段在冲突更新分支中被刻意保留),相关逻辑见 storage.service.ts 的putObject方法。

3.2 POST:服务端自动生成唯一 Key

POST /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.ts):

  • MIME 魔数检测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(当前源码钳制在 1~1000)
offset分页偏移量0
search按文件名(Key)模糊搜索

curl 示例:

# Windows PowerShell: 使用 curl.exe curl -X GET "http://localhost:7130/api/storage/buckets/avatars/objects?limit=10&prefix=users/" \ -H "Authorization: Bearer <token>"

原文档记载的响应示例(包含分页头X-Total-CountX-PageX-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 1:PUT 指定 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 2:POST 自动生成 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。数据库记录中保存响应返回的keyurl字段,读取时即可直接拼装<img src>

从数据流上看,上传响应中的sizemimeTypeuploadedAt与数据库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 与既有记录冲突(数据库唯一约束 23505)
STORAGE_NOT_FOUND404Bucket / 对象不存在
STORAGE_INVALID_PARAMETER400Bucket 名或 Key 非法、请求体不符合 schema、expiresIn非数字等
STORAGE_PERMISSION_DENIED403RLS 拒绝写入(数据库权限错误 42501),或缺少用户上下文

编写客户端时,建议始终以error代码(而非 message 文本)做分支判断,并善用nextActions字段向用户给出可执行的下一步指引。

7. 重要规则与最佳实践清单

最后,汇总原文档的“Important Rules”并结合源码补充实践建议:

  1. 对象操作规范

    • 上传一律使用 multipart/form-data,字段名固定为file
    • 数据库只存元数据,不存二进制;元数据建议用json列类型;
    • 响应中的url是绝对 URL,直接用于前端展示与 fetch,切勿二次拼接 host。
  2. Bucket 命名

    • 只允许字母、数字、连字符、下划线(源码正则^[a-zA-Z0-9_-]+$);
    • 遵循文档约定:不以_开头,保持命名整洁;
    • 命名一经创建即被复用,创建同名 Bucket 会得到 409。
  3. 鉴权与工具分工

    • Bucket 管理推荐使用 MCP 工具(create-bucket/list-buckets/delete-bucket),或使用等价的管理员 REST 端点;
    • 对象操作统一走 REST API;
    • 所有写操作都需要鉴权;公开 Bucket 的下载免鉴权;
    • API Key 适用于 MCP 测试与机器场景,生产环境优先使用会话 token。
  4. 安全与扩展能力(源码补充)

    • 上传内置 MIME 魔数校验,可执行类型自动降级为application/octet-stream,下载端对不安全 MIME 强制attachment
    • 存储配置接口GET/PUT /api/storage/config(管理员)可动态调整全局最大文件大小(maxFileSizeMb,允许 1~200 MB,默认 50 MB,参见 storage-config.service.ts);
    • 如需超大文件直传 S3,可使用当前源码提供的上传/下载策略接口(POST /api/storage/buckets/:bucketName/upload-strategyGET /api/storage/buckets/:bucketName/download-strategy/objects/*POST .../confirm-upload),获取 presigned URL 后在客户端直传/直下,绕过网关代理;
    • 面向 S3 生态的S3_BUCKET部署还提供/storage/v1/s3网关与访问密钥管理接口(GET /api/storage/s3/configPOST/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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 15:37:57

深圳全网站建设公司速查手册:域名服务器选型避坑

深圳全网站建设公司速查手册:域名服务器选型避坑 域名和服务器,这俩词儿是不是让你头大?很多老板找深圳全网站建设公司时,一听到“云主机”、“CDN”、“DNS解析”就懵圈。别慌,这篇速查手册就是为你写的。咱们不整虚的,直接上干货。在珠三角做业务,网络环境的稳定性直接决定客户体验。如果你还在纠结是买阿里…

作者头像 李华
网站建设 2026/9/15 15:33:42

如何用 MNN qwen3_tts_demo 运行 Qwen3-TTS 文本转语音?

如何用 MNN qwen3_tts_demo 运行 Qwen3-TTS 文本转语音&#xff1f; 【免费下载链接】MNN MNN: A blazing-fast, lightweight inference engine battle-tested by Alibaba, powering high-performance on-device LLMs and Edge AI. 项目地址: https://gitcode.com/GitHub_Tre…

作者头像 李华
网站建设 2026/9/15 15:33:41

明医(MING):中文医疗领域大模型本地部署与临床适配指南

简介&#xff1a;明医&#xff08;MING&#xff09;是一款专为中文医疗问诊场景研发的垂直领域大模型&#xff0c;融合多模态技术与人工智能能力&#xff0c;面向医疗AI研究者、算法工程师及临床信息化开发者&#xff0c;旨在解决专业医学语义理解、跨模态病历分析与轻量化部署…

作者头像 李华