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