Files
gongxue-base/docs/refactor/object-storage.md
2026-06-29 06:14:58 +08:00

7.1 KiB
Raw Blame History

对象存储接入说明

更新时间2026-06-29

目标

题库里的图片、PDF、视频、音频、资料包等媒体资源统一走 content_assets 台账和后端签名接口。前端不直接保存或读取云厂商密钥,也不直接拼接私有资源 URL。题目视频播放还需要经过 POST /api/videos/play 校验 SVIP 或视频次数权益后下发短期签名 URL。

已接入的 provider

  • local_dev:本地开发占位签名,便于前后端联调。
  • aliyun_oss:阿里云 OSS 官方 Node.js SDK signatureUrl
  • tencent_cos:腾讯云 COS XML API V5 签名 URL服务端用 Node crypto 实现,避免引入当前 COS Node SDK 的高危依赖。
  • supabase_storageSupabase Storage 官方 createSignedUploadUrl / createSignedUrl 语义。
  • external_url:外部公开或厂商托管 URL只允许作为已管理资源的下载地址不支持后端直传签名。

API

后台申请上传签名:

POST /api/tenant-content/assets/sign-upload

后台登记资源草稿:

PUT /api/tenant-content/assets

后台确认上传并发布:

POST /api/tenant-content/assets/confirm-upload

学生端下载:

GET /api/catalog/assets/download?assetId=...

学生端预览:

GET /api/catalog/assets/preview?assetId=...

学生端视频播放:

POST /api/videos/play

后台管理员下载:

POST /api/tenant-content/assets/sign-download

后台管理员预览:

POST /api/tenant-content/assets/sign-preview

后台资源复检 worker

npm --workspace @tiku-saas/worker run assets:once

标准上传流程

后台前端上传 PDF、图片、视频或资料包时必须走下面流程

  1. 调用 POST /api/tenant-content/assets/sign-upload 申请短期上传 URL。
  2. 前端使用返回的 upload.urlupload.headers 直传对象存储。
  3. 调用 PUT /api/tenant-content/assets 登记资源台账。托管对象默认进入 status=draftuploadStatus=pending
  4. 调用 POST /api/tenant-content/assets/confirm-upload由后端读取对象元数据并比对大小、MIME、SHA-256。
  5. 校验通过且 publish=true 时,后端将资源置为 status=activeuploadStatus=verified
  6. 学生端只能下载或预览 active + verified 的托管对象资源。
  7. 生产环境定时运行 assets worker复检 pending/verified 托管对象的大小、MIME、SHA-256 等元数据。
  8. 如果复检发现对象丢失、跨租户 objectKey、大小/MIME/checksum 不一致worker 会把资源置为 uploadStatus=failed,并将 active 资源退回 draft,同时写入 security_flags.assetRecheckFailed=trueaudit_logs

确认上传示例:

{
  "assetId": "00000000-0000-0000-0000-000000000000",
  "fileSizeBytes": 4096,
  "mimeType": "application/pdf",
  "checksumSha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "publish": true
}

注意:

  • local_dev 用声明的元数据模拟对象 HEAD方便本地和集成测试。
  • aliyun_oss 使用 OSS 对象 HEAD 元数据。
  • tencent_cos 使用 COS HEAD Object 预签名请求读取元数据。
  • supabase_storage 使用 Storage list metadata 做最小存在性/大小校验。
  • 如果 provider 无法返回 SHA-256后端会记录 checksumUnavailable=true,生产建议上传时写入对象自定义元数据,例如 x-oss-meta-sha256x-cos-meta-sha256
  • worker 的复检证据写入 verification_details.assetWorker,包含 lastCheckedAtlastResultexpectedobservedissues,便于租户后台定位资源异常。

安全规则

  • 只有租户内容维护权限用户可以申请上传签名。
  • objectKey 默认必须以当前 tenantId/ 开头,防止跨租户覆盖或读取。
  • 禁止 ..、反斜杠、编码斜杠等危险 object key。
  • 上传会校验 MIME 类型和文件大小。
  • 托管对象资源未确认前不能发布为 active,学生端不可下载。
  • PDF/图片预览使用 inline 签名,不等同于长期公开 URL。
  • 下载和视频播放必须先经过 API 权限判断,再下发短期签名 URL。
  • 云厂商 AccessKey、SecretKey、Service Role Key 只存在服务端环境变量,不返回前端。
  • content_assets 是资源唯一台账,前端不得绕过台账直接访问私有 bucket。
  • 前端不能把 uploadStatus=failedstatus=draft 的资源继续展示为可下载;列表仍返回时应展示“资料处理中”或“资源异常已下架”,真正下载/预览会被后端拒绝。

环境变量

通用:

STORAGE_DEFAULT_PROVIDER=local_dev
STORAGE_DEFAULT_BUCKET=tenant-assets
STORAGE_PUBLIC_BASE_URL=
STORAGE_MAX_UPLOAD_BYTES=524288000
STORAGE_ALLOWED_MIME_PREFIXES=image/,video/,audio/
STORAGE_ALLOWED_MIME_TYPES=application/pdf,application/json,application/zip,application/x-zip-compressed,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-powerpoint,application/vnd.openxmlformats-officedocument.presentationml.presentation,application/octet-stream,text/plain,text/markdown,text/csv
STORAGE_REQUIRE_TENANT_PREFIX=true

阿里云 OSS

ALIYUN_OSS_REGION=oss-cn-hangzhou
ALIYUN_OSS_ENDPOINT=
ALIYUN_OSS_ACCESS_KEY_ID=
ALIYUN_OSS_ACCESS_KEY_SECRET=
ALIYUN_OSS_STS_TOKEN=
ALIYUN_OSS_INTERNAL=false

腾讯云 COS

TENCENT_COS_REGION=ap-shanghai
TENCENT_COS_APP_ID=
TENCENT_COS_SECRET_ID=
TENCENT_COS_SECRET_KEY=
TENCENT_COS_SECURITY_TOKEN=

Supabase Storage

SUPABASE_STORAGE_URL=https://your-project.supabase.co/storage/v1
SUPABASE_STORAGE_SERVICE_KEY=

assets worker

WORKER_ASSET_BATCH_SIZE=50
WORKER_ASSET_MIN_AGE_SECONDS=300
WORKER_ASSET_RECHECK_INTERVAL_SECONDS=86400
WORKER_ASSET_REQUEST_TIMEOUT_MS=10000

生产建议

  • 阿里云和腾讯云生产环境优先用 STS/临时密钥或 RAM/CAM 最小权限账号。
  • bucket 默认私有,公开资源也建议先经过 CDN/防盗链策略,不让前端直接持有写权限。
  • 图片、PDF、视频分别设置合理的 CORS只允许前端域名和小程序业务域名访问。
  • 开启对象版本控制、生命周期、跨区域复制或定时备份,满足后续容灾要求。
  • 视频资源已接入 SVIP/播放次数校验、短期签名和播放日志生产阶段继续补转码、动态水印、CDN 防盗链和播放统计。
  • 大文件上传已经支持 API 即时确认和 worker 元数据复检;后续继续补杀毒、转码、水印和 CDN 刷新。

官方依据

  • 阿里云 OSS Node.js SDK 支持通过 signatureUrl 为上传/下载生成带过期时间的签名 URL并可通过对象 HEAD 读取元数据。
  • 腾讯云 COS XML API V5 签名由 q-sign-algorithmq-akq-sign-timeq-key-timeq-header-listq-url-param-listq-signature 等字段组成,可用于预签名 URL 和 HEAD Object。
  • Supabase Storage 提供 createSignedUploadUrlcreateSignedUrl,分别用于签名上传和签名下载;私有 bucket 仍应配合 RLS、服务端权限控制和资源台账。