Files 文件服务
Files 是 OPS 的公共文件服务,统一管理文件元数据,并通过阿里云 OSS 预签名地址让调用方直传文件。调用方只选择已配置的命名空间,不能指定 OSS 厂商、Bucket 或对象路径。
当前仅支持阿里云 OSS。服务不在本地保存文件,已完成上传的文件持续保留,临时文件由调用方在使用结束后主动删除;只有超时且未完成的上传会被后台任务自动清理。
主要能力
- 生成 OSS 预签名 PUT 地址,文件不经过 Files 服务中转。
- 校验命名空间、扩展名、文件大小和上传后的对象大小。
- 记录文件归属、状态、对象路径、ETag 和公开访问地址。
- 支持 JWT 用户调用和内部服务调用,二者数据相互隔离。
- 支持查询与删除本人或本服务创建的文件。
- 自动清理超过预签名有效期但未完成的上传。
运行依赖
- Go 1.25.1
- PostgreSQL
- Redis
- 阿里云 OSS Bucket
配置
配置文件位于 etc:
etc/files_dev.yaml
etc/files_test.yaml
etc/files_prod.yaml
运行环境由 BSM_RuntimeMode 决定,默认值为 dev。服务读取 ${BSM_Prefix}/etc/files_<mode>.yaml;开发模式未设置 BSM_Prefix 时,使用当前工作目录。
核心配置示例:
ServiceClients:
assets: <assets 调用密钥>
dc-control: <dc-control 调用密钥>
visual: <visual 调用密钥>
ObjectStorage:
Provider: aliyun
Endpoint: https://oss-cn-beijing.aliyuncs.com
Region: cn-beijing
Bucket: ops-app
PublicBaseURL: https://ops-app.oss-cn-beijing.aliyuncs.com
AccessKeyID: ${FILES_OSS_ACCESS_KEY_ID}
AccessKeySecret: ${FILES_OSS_ACCESS_KEY_SECRET}
PresignTTLSeconds: 600
Namespaces:
reports:
Prefix: reports
MaxSizeMB: 512
AllowedExtensions: [.csv, .xlsx, .json, .pdf, .zip]
Cleanup:
IntervalSeconds: 600
配置说明:
ServiceClients:允许调用内部接口的服务名和密钥,服务名必须与请求头Service-Name完全一致。Provider:当前只能填写aliyun。Endpoint:OSS SDK 请求地址,不包含 Bucket 名称。PublicBaseURL:完成上传后返回给调用方的公开访问地址前缀。PresignTTLSeconds:上传地址有效期,允许范围为 60~3600 秒。Namespaces:调用方可使用的命名空间,以及对应的目录、大小和扩展名限制。Cleanup.IntervalSeconds:扫描并清理超时未完成上传的时间间隔。
OSS 凭证必须通过环境变量提供:
$env:FILES_OSS_ACCESS_KEY_ID = '<AccessKey ID>'
$env:FILES_OSS_ACCESS_KEY_SECRET = '<AccessKey Secret>'
使用用户接口时,还要保证 Files 与 JWT 签发方使用相同密钥:
$env:BSM_JwtSecretKey = '<与 JWT 签发方一致的密钥>'
不要将 AccessKey、数据库密码或生产环境服务密钥写入 README 或提交到代码仓库。
本地启动
在 files 目录执行:
$env:BSM_RuntimeMode = 'dev'
go run ./cmd/main/main.go
默认监听配置文件中的 12452 端口。启动时会自动创建或更新 files_object 表。
如需只执行表结构迁移:
go run ./cmd/cli/main.go migrate
健康检查:
HEAD /
GET /Files/v1/ping/hello
构建
构建当前 Windows 环境程序:
go build -o build/files.exe ./cmd/main/main.go
打包 Linux amd64 程序:
powershell -ExecutionPolicy Bypass -File ./scripts/pack.ps1
产物为 build/ops-files。
调用鉴权
文件接口提供两套路径,功能和请求结构相同。
用户接口使用 JWT:
Authorization: <JWT 原始字符串>
JWT 直接放入 Authorization,不要添加 Bearer 前缀。
内部服务接口使用服务名和独立密钥:
Service-Name: assets
Secret-Key: <与 ServiceClients.assets 一致的值>
服务密钥只用于服务之间调用,不是阿里云 AccessKey。调用方不能通过请求选择 OSS 厂商或 Bucket。
文件接口
| 操作 | 用户接口 | 内部服务接口 |
|---|---|---|
| 初始化上传 | POST /Files/v1/uploads/init |
POST /Files/v1/internal/uploads/init |
| 确认上传完成 | POST /Files/v1/uploads/:file_id/complete |
POST /Files/v1/internal/uploads/:file_id/complete |
| 查询文件 | GET /Files/v1/files/:file_id |
GET /Files/v1/internal/files/:file_id |
| 删除文件 | DELETE /Files/v1/files/:file_id |
DELETE /Files/v1/internal/files/:file_id |
同一个文件只能由创建它的用户或内部服务访问。用户接口和内部服务接口之间不能交叉访问文件。
上传流程
- 调用初始化接口:
POST /Files/v1/internal/uploads/init
Content-Type: application/json
Service-Name: assets
Secret-Key: <服务密钥>
{
"namespace": "assets",
"filename": "example.png",
"size": 102400,
"content_type": "image/png"
}
成功结果的 details 中包含:
{
"file_id": "文件标识",
"object_key": "assets/年/月/文件标识.png",
"upload": {
"method": "PUT",
"url": "OSS 预签名地址",
"headers": {
"接口返回的请求头": "接口返回的值"
},
"expires_at": "过期时间"
}
}
-
使用
upload.method和upload.url将文件内容直接上传到 OSS,并原样携带upload.headers。此请求不携带 Files 的 JWT、Service-Name或Secret-Key。 -
OSS 上传成功后调用确认接口:
POST /Files/v1/internal/uploads/<file_id>/complete
Service-Name: assets
Secret-Key: <服务密钥>
确认接口会检查 OSS 对象是否存在以及实际大小是否与初始化请求一致。成功后返回文件信息,其中 url 是公开访问地址。
查询和删除
GET /Files/v1/internal/files/<file_id>
DELETE /Files/v1/internal/files/<file_id>
删除接口同时删除 OSS 对象和文件元数据。处于 ready 状态的临时文件不会自动删除,调用方必须在业务使用结束后主动调用删除接口。
响应约定
业务接口使用统一响应结构:
{
"code": 0,
"message": "",
"details": {},
"timeseq": 0
}
code = 0表示成功。- 业务错误通常仍返回 HTTP 200,通过非零
code判断失败。 - JWT 或内部服务鉴权失败返回 HTTP 401。
OSS 前置设置
当前 PublicBaseURL 使用 OSS Bucket 公网域名,因此 Bucket 需要设置为“公共读”,不能设置为“公共读写”。如果由浏览器直接上传,还需要给 Bucket 配置 CORS:
来源:实际前端域名
Methods:PUT、GET、HEAD
允许 Headers:*
暴露 Headers:ETag、x-oss-request-id
生产环境应使用 RAM 用户的 AccessKey,并只授予目标 Bucket 所需的对象上传、查询和删除权限。