VibeAny Docs
构建

存储与 AI 图片生成

配置 S3 兼容存储和图片服务商,并理解其中的安全、积分和持久化边界。

对象存储和 AI 图片生成是两个独立集成。没有存储时仍可生成图片,但持久的生成图片 URL 需要同时启用两者。

配置对象存储

存储选择器支持 r2s3minio,它们都会组合到同一个 S3 兼容适配器。

Cloudflare R2 示例:

STORAGE_PROVIDER=r2
S3_ENDPOINT=https://account-id.r2.cloudflarestorage.com
S3_ACCESS_KEY_ID=replace_with_access_key_id
S3_SECRET_ACCESS_KEY=replace_with_secret_access_key
S3_BUCKET_NAME=vibeany-files
S3_REGION=auto
S3_PUBLIC_URL=https://cdn.example.com

上述端点、访问密钥 ID、私有访问密钥和存储桶名称四项 S3 配置必须全部填写或全部留空。全部留空时,运行时存储关闭;只填写部分配置时,系统会判定配置不完整。npm run preflight 还会把已经设置的 STORAGE_PROVIDER 视为启用存储的意图,因此决定暂缓存储配置时,应删除或注释该变量。

只使用私有文件时,S3_PUBLIC_URL 可以不配置;公开文件则强烈建议配置。适配器不会替你创建公开存储桶策略、自定义域名、对象 ACL 或私有前缀策略。access 只控制应用返回公开 URL 还是签名 URL,不会改变服务商侧的对象可见性。

不要把敏感私有对象放入通过 S3_PUBLIC_URL 匿名公开的存储桶。如果所有对象都是私有的,应保持整个存储桶私有。如果产品同时需要公开和私有对象,请使用相互独立的公开存储桶和私有存储桶;也可以实现并验证等效的服务商/CDN 访问策略,确保每个私有前缀都无法匿名访问。Starter 当前只配置一个存储桶,因此双存储桶设计需要扩展应用。

准备 R2 或 S3 兼容存储桶

启用浏览器上传前:

  1. 创建存储桶;
  2. 创建只拥有必要存储桶操作权限的凭证;
  3. 在存储桶 CORS 中允许应用源地址和所需的 PUT 请求头;
  4. 只为确实要公开的对象配置公开域名或自定义域名;
  5. 在真实服务商上验证上传、元数据查询、下载和删除。

仓库的 Docker Compose 不会启动 MinIO,当前适配器也没有 S3_FORCE_PATH_STYLE 设置。应把 MinIO 视为需要单独配置和验证的集成,而不是内置的零配置服务。

浏览器上传流程

已登录用户的上传分三步:

  1. 调用 POST /api/storage/upload,传入 filenamecontentTypecontentLength,以及可选的 folderaccess
  2. 使用 PUT 和相同的内容类型,把文件字节直接上传到返回的预签名 URL;
  3. 使用返回的 fileId 调用 POST /api/storage/confirm

确认步骤会检查对象是否存在,并验证文件大小和内容类型是否与待确认的数据库记录一致。

默认设置:

设置默认值
访问级别private
上传 URL 有效期5 分钟
私有下载 URL 有效期1 小时
最大文件大小10 MB
允许的类型JPEG、PNG、GIF、WebP、SVG、PDF
文件夹uploads

私有对象使用按用户隔离的对象键,应用会返回签名下载 URL。这个响应行为并不能保证存储层隔离。公开上传默认使用不可猜测的对象名;配置公开基础 URL 后,会返回 S3_PUBLIC_URL/<key>

默认内容类型允许列表包含 SVG。不可信的 SVG 可能包含可执行脚本,应将其视为活动内容:要么从允许类型中移除 SVG,要么通过隔离的资源域名提供文件,并使用严格的响应头,且不能携带应用 Cookie。

删除文件并不能跨对象存储和 PostgreSQL 保证事务一致性。当前适配器将服务商删除操作视为尽力而为,因此生产系统应监控并核对孤儿对象。重复确认同一次上传会返回冲突响应,而不是幂等成功。

启用图片生成

NEXT_PUBLIC_ENABLE_IMAGE_GENERATION=true

# Optional explicit selector: fal, modelscope, or dashscope
IMAGE_PROVIDER=fal
FAL_KEY=replace_with_provider_key

# Alternatives
# MODELSCOPE_API_KEY=replace_with_provider_key
# DASHSCOPE_API_KEY=replace_with_provider_key

未设置 IMAGE_PROVIDER 时,选择优先级为:

  1. Fal
  2. ModelScope
  3. DashScope

显式选择服务商时,必须同时提供与之匹配的凭证;否则预检会失败,图片生成功能也不可用。执行和回退只会使用已经配置的服务商。FAL_KEYMODELSCOPE_API_KEYDASHSCOPE_API_KEY 等服务商密钥必须只在服务端使用,不能暴露给浏览器。

公开入口包括:

  • 页面:/{locale}/demo/image-generator
  • API:POST /api/generate-image

功能开关关闭时 API 返回 404;请求未登录时返回 401;积分不足时返回 402;输入无效时返回 400;无法完成生成时返回 500。

积分、重试与持久化

  • 每次请求可生成 1–4 张图片。
  • 每张图片消耗 10 积分。
  • 系统会在调用模型服务商前,以原子操作扣除积分。
  • 服务商生成失败时,会通过退款交易返还该请求的全部积分。
  • 路由器会重试可重试错误,也可以切换到其他已配置服务商。
  • 无效请求和身份验证错误不会被主动重试。

生成成功后,应用会尝试获取每张远程图片,并将其保存为 generated 文件夹下的公开对象。该持久化步骤会尽力执行

  • 存储关闭时,返回服务商 URL;
  • 获取或保存失败时,返回服务商 URL;
  • 存储失败不会返还积分,因为图片已经成功生成;
  • 服务商 URL 可能是临时的,因此在承诺持久图片库前必须配置存储。

验证集成

npm run preflight
npm run test:unit
npm run test:credit-ledger:integration
npm run build

然后在真实运行环境中验证:

  1. 功能关闭时,入口被隐藏并返回 404;
  2. 已配置服务商可以生成 1 张和 4 张图片;
  3. 余额不足时返回 402,且余额不会变成负数;
  4. 模拟服务商失败后,已扣积分得到恢复;
  5. 生成文件可以通过已配置的公开域名读取;
  6. 存储失败时回退到服务商 URL,并能从日志中观察到;
  7. 通过每一个公开域名或 CDN 域名直接匿名请求私有对象键时,都应返回 403 或 404;否则,上线前必须把私有数据迁移到独立的私有存储桶。

请阅读自定义 Starter,了解商品目录和服务商扩展规则。

本页目录