8.5 KiB
工作室画廊 COS 接入配置说明
本文档对应当前仓库当前实现。
现在已经不再使用 STS AssumeRole。
当前方案改为:
- 服务端使用长期密钥直接签发 COS POST Policy
- 管理中心小程序拿到表单签名后直传 COS
- 工作室配置中的
logo、bannerUrl、photos保存最终可访问 URL
当前实现代码入口:
packages/server/src/studio/studio-upload.service.tspackages/server/src/studio/studio.controller.tspackages/app/src/utils/studio-upload.tspackages/app/src/pages/admin/studio.vue
一、整体链路
- 管理中心点击上传图片。
- 小程序请求服务端
POST /api/admin/studio/upload-credentials。 - 服务端用
COS_SECRET_ID、COS_SECRET_KEY直接生成一组 POST Policy 表单字段。 - 服务端把
uploadUrl、key、formData、fileUrl、expiresAt返回给小程序。 - 小程序使用
uni.uploadFile直接上传到 COS。 - 上传成功后,把 URL 保存到工作室配置,再调用
PUT /api/admin/studio/info落库。
这个方案没有临时密钥,也没有角色扮演。 安全边界来自两层:
- 服务端只为单个对象 key 签发一次表单策略
- 表单策略有明确过期时间,过期后自动失效
二、这个方案的本质
你现在选的是“服务端代签名”的直传方案。 它和 STS 的差别是:
- STS:给前端一段时间内可用的短期密钥
- 当前方案:不给前端密钥,只给前端一个短时有效的上传表单签名
所以结论很直接:
- 仍然有有效期
- 但有效期作用在 POST Policy 上,不是作用在临时密钥上
当前代码里默认有效期是 1800 秒。
环境变量:
COS_UPLOAD_DURATION_SECONDS
当前实现限制范围:
- 最短
300秒 - 最长
7200秒
三、你现在真正需要准备的东西
先确认下面几个信息:
- COS Bucket 名称,例如
plates-1251306435 - COS 所在地域,例如
ap-guangzhou - 服务端使用的 COS 长期密钥
SecretId/SecretKey - 图片上传前缀,例如
mp/studio - 图片访问域名
建议约定:
- Bucket:
plates-1251306435 - Region:
ap-guangzhou - Prefix:
mp/studio
四、COS 控制台配置
1. 创建或确认 Bucket
控制台路径:对象存储 COS
建议:
- 地域选
广州或你当前实际地域 - 存储类型标准存储即可
- Bucket 名称和环境变量保持完全一致
2. 图片访问方式
当前实现保存的是直接图片 URL。 所以图片必须能被小程序和前台直接访问。
你有两种方式:
- 直接使用 COS 源站并允许读
- 配 CDN / 自定义域名并让这个域名可直接访问图片
如果你什么都不配,上传成功后图片可能打不开。
最直接做法:
- 让这个图片 Bucket 对外可读
更稳妥做法:
- 单独图片 Bucket
- 用 CDN 域名做
COS_PUBLIC_BASE_URL
3. 微信小程序合法域名
微信公众平台需要补白名单:
request 合法域名:你的后端 API 域名uploadFile 合法域名:https://<bucket>.cos.<region>.myqcloud.comdownloadFile 合法域名:图片访问域名
如果图片访问也走 COS 源站,那么 downloadFile 合法域名 同样加:
https://<bucket>.cos.<region>.myqcloud.com
例如:
https://focus.richarjiang.comhttps://plates-1251306435.cos.ap-guangzhou.myqcloud.com
五、服务端账号需要什么权限
现在已经不需要:
- STS
- CAM 角色
AssumeRole- 角色信任策略
COS_UPLOAD_ROLE_ARN
现在服务端只需要一对可以给目标 Bucket 生成上传签名的长期密钥。
最简单的做法是:
- 用你的主账号密钥
但生产上更合理的是:
- 建一个专用 CAM 用户,只给这个 Bucket 上传相关权限
推荐 CAM 用户权限策略
如果你要建专用 CAM 用户,给它绑定下面这类策略即可。
把下面真实值替换成你的实际资源:
- 地域:
ap-guangzhou - AppId:
1251306435 - Bucket:
plates-1251306435 - Prefix:
mp/studio
{
"version": "2.0",
"statement": [
{
"effect": "allow",
"action": [
"name/cos:PutObject",
"name/cos:PostObject"
],
"resource": [
"qcs::cos:ap-guangzhou:uid/1251306435:plates-1251306435/mp/studio/*"
]
}
]
}
如果你后续还要服务端删除对象,再补:
name/cos:DeleteObject
当前仓库实现不需要删除对象,所以先不要额外放大权限。
六、服务端环境变量
把下面变量配置到 packages/server/.env 或线上环境:
COS_SECRET_ID=your-cos-secret-id
COS_SECRET_KEY=your-cos-secret-key
COS_BUCKET=plates-1251306435
COS_REGION=ap-guangzhou
COS_PUBLIC_BASE_URL=https://plates-1251306435.cos.ap-guangzhou.myqcloud.com
COS_UPLOAD_PREFIX=mp/studio
COS_UPLOAD_DURATION_SECONDS=1800
各字段含义:
COS_SECRET_ID:用于签发 POST Policy 的长期密钥 IDCOS_SECRET_KEY:用于签发 POST Policy 的长期密钥 KeyCOS_BUCKET:上传目标 BucketCOS_REGION:Bucket 地域COS_PUBLIC_BASE_URL:最终展示图片的访问域名COS_UPLOAD_PREFIX:统一对象前缀COS_UPLOAD_DURATION_SECONDS:Policy 有效期秒数
现在可以删除或忽略这些旧配置:
COS_UPLOAD_ROLE_ARNCOS_APP_IDCOS_UPLOAD_ROLE_SESSION_NAME
它们对当前实现已经没用。
七、控制台操作清单
按这个顺序做:
- 确认 COS Bucket 已存在。
- 确认图片访问域名对外可读。
- 在微信公众平台加好
request/uploadFile/downloadFile合法域名。 - 准备一对 COS 长期密钥。
- 把
COS_SECRET_ID、COS_SECRET_KEY、COS_BUCKET、COS_REGION、COS_PUBLIC_BASE_URL、COS_UPLOAD_PREFIX配到服务端。 - 重启服务端。
- 在管理中心上传一张图片测试。
八、接口返回内容说明
请求:
POST /api/admin/studio/upload-credentials
Content-Type: application/json
Authorization: Bearer <admin-token>
{
"fileName": "demo.jpg",
"contentType": "image/jpeg",
"assetType": "gallery"
}
正常返回会包含:
uploadUrlfileUrlkeyassetTypeexpiresAtformData
formData 里会有这些字段:
keypolicysuccess_action_statusContent-Typeq-sign-algorithmq-akq-key-timeq-sign-timeq-signature
这就是小程序直传需要的全部内容。
九、怎么验证是否配置正确
1. 接口层验证
调用 POST /api/admin/studio/upload-credentials。
如果成功,说明:
- 服务端长期密钥有效
- 服务端已经能正确签发 policy
2. 上传层验证
在管理中心上传一张图,检查:
- COS Bucket 下是否出现对象
- 返回的
fileUrl浏览器是否能直接访问 - 保存工作室设置后首页是否显示该图
3. 失败时怎么定位
如果 upload-credentials 接口失败,优先检查:
COS_SECRET_ID/COS_SECRET_KEY是否正确COS_BUCKET/COS_REGION是否正确- 服务端是否已经加载最新环境变量
如果接口成功但上传失败,优先检查:
- 小程序
uploadFile 合法域名是否正确 - Bucket 权限策略是否允许当前长期密钥上传到该前缀
Content-Type是否被策略条件限制住
如果上传成功但图片打不开,优先检查:
- Bucket 或图片域名是否可公网访问
COS_PUBLIC_BASE_URL是否正确- 小程序
downloadFile 合法域名是否正确
十、当前实现的边界
当前仓库实现边界如下:
- 只支持
jpg、jpeg、png、webp、heic、heif - 单次上传大小上限
10MB - 只支持普通表单直传,不支持分片上传
- 删除工作室图片时,只会从数据库配置里移除 URL,不会删除 COS 历史对象
最后一条是故意保守设计。 原因很简单:
- 先保证配置删除安全
- 避免误删真实文件
如果以后要做“删配置时同步删对象”,那时再单独加 DeleteObject 权限。
十一、初始化工作室画廊
如果你要把现在手工写死的图片 URL 一次性写入数据库,执行:
pnpm --filter @mp-pilates/server studio:seed-gallery
脚本文件:
packages/server/prisma/update-studio-gallery.ts
十二、建议的生产做法
如果你后面要长期维护,建议:
- 图片单独放一个 Bucket。
- 长期密钥不要直接用主账号,换成专用 CAM 用户。
- 对专用 CAM 用户只给
mp/studio/*前缀上传权限。 - 用 CDN 域名作为
COS_PUBLIC_BASE_URL。
这样后面扩展、迁移、审计都会更稳。