8.6 KiB
文件上传服务接口文档
本文件描述文件上传服务的全部接口,包含**后台管理(
/api/admin/files)与小程序端(/api/ma/files)**两套入口。两套入口复用同一套业务逻辑(internal/service/file)与存储后端,仅鉴权方式不同。公共约定(统一响应、错误码、中间件)见 admin-api.md 与 miniapp-api.md。
1. 概述
- 后台入口基础路径:
/api/admin/files - 小程序入口基础路径:
/api/ma/files - 鉴权:
- 后台接口需携带
Authorization: Bearer <JWT>(由AdminAuth校验);当前仅要求已登录管理员,未做细粒度权限码守卫。 - 小程序接口需携带
X-App-ID(MiniAppAuth校验小程序被纳管且启用)与Authorization: Bearer <token>/X-Session-Token(由MiniUserAuth校验登录态)。
- 后台接口需携带
- 统一响应:与全局一致,成功
code=0;失败通过code区分(见 §5)。 - 上传者身份:由路由分组自动解析——后台为管理员
admin_id,小程序为当前登录用户user_id,并写入文件记录(uploader_type/uploader_id)。未认证调用上传返回40100。
2. 配置与存储
- 配置位于
configs/config.yaml的upload段:storage_type(当前local)、local_path、base_url、max_size(字节)、allowed_ext(扩展名白名单,空表示不限制)。 - 存储后端抽象为
internal/pkg/storage.Storage接口,当前提供本地磁盘实现(local);minio等对象存储预留,后续仅需扩展storage.New的case分支与对应实现,业务层无感。 - 本地模式下,文件按日期分片落盘:
{local_path}/{yyyy}/{mm}/{dd}/<rand>.<ext>;返回的url形如{base_url}/{yyyy}/{mm}/{dd}/<rand>.<ext>,由r.Static对外提供访问(与GET .../raw等价)。 - 建表脚本见
scripts/003_files.sql(执行顺序在 RBAC、小程序脚本之后)。
3. 接口列表
3.1 GET /api/admin/files(后台文件列表)
分页列出文件,支持按上传者类型/ID 与关键字过滤,供后台「文件管理」栏目使用。
请求头:Authorization: Bearer <JWT>。
查询参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
page |
int | 否 | 1 |
页码,从 1 开始。 |
size |
int | 否 | 20 |
每页条数。 |
keyword |
string | 否 | 空 | 模糊匹配 origin_name 或 filename。 |
uploader_type |
string | 否 | 空 | 按上传者类型过滤:admin / ma;空表示不限。 |
uploader_id |
uint64 | 否 | 0 |
按上传者主键过滤;0 表示不限。 |
响应
{
"code": 0,
"msg": "ok",
"data": {
"list": [
{
"id": 1,
"storage_key": "2026/10/27/3f2a...c1.png",
"filename": "3f2a...c1.png",
"origin_name": "我的图片.png",
"mime_type": "image/png",
"ext": "png",
"size": 12345,
"uploader_type": "admin",
"uploader_id": 1,
"url": "/files/static/2026/10/27/3f2a...c1.png",
"created_at": "2026-10-27 22:00:00"
}
],
"total": 1,
"page": 1,
"size": 20
}
}
错误码:未认证 40100;查询失败 50000。
curl 示例
curl "http://localhost:8080/api/admin/files?keyword=png&page=1&size=20" \
-H "Authorization: Bearer <JWT>"
3.2 POST /api/admin/files/upload(后台上传)
以 multipart/form-data 上传单个文件,表单字段名为 file。
请求头:Authorization: Bearer <JWT>;Content-Type: multipart/form-data。
表单字段
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
file |
file | 是 | 待上传文件。大小受 max_size 限制,扩展名受 allowed_ext 限制。 |
响应
{
"code": 0,
"msg": "ok",
"data": {
"id": 1,
"url": "/files/static/2026/10/27/3f2a...c1.png",
"filename": "3f2a...c1.png",
"origin_name": "我的图片.png",
"mime_type": "image/png",
"size": 12345
}
}
错误码:缺文件 40000;未认证 40100;超限 41300;类型不允许 41500;落盘/落库失败 50000。
curl 示例
curl -X POST http://localhost:8080/api/admin/files/upload \
-H "Authorization: Bearer <JWT>" \
-F "file=@/path/to/我的图片.png"
3.3 GET /api/admin/files/:id(后台查询元数据)
查询指定文件的元数据记录。
路径参数:id(uint64)。响应:返回 File 对象(见 §4);不存在返回 40400。
3.4 GET /api/admin/files/:id/raw(后台获取文件流)
以流形式返回文件内容,按 mime_type 设置 Content-Type,Content-Disposition: inline,可用于浏览器预览/下载。
路径参数:id(uint64)。响应:二进制流;不存在返回 40400。
也可直接通过上传返回的
url访问(本地模式由静态目录服务,等效于本接口)。
3.5 DELETE /api/admin/files/:id(后台删除)
删除文件:同时清理存储内容与 files 表元数据记录。
路径参数:id(uint64)。响应:{ "code": 0, "msg": "ok", "data": { "id": 1 } };不存在返回 40400。
3.6 POST /api/ma/files/upload(小程序上传)
与 3.2 一致,但鉴权为小程序登录态。
请求头:X-App-ID: <AppID> + Authorization: Bearer <token>(或 X-Session-Token)。
curl 示例
curl -X POST http://localhost:8080/api/ma/files/upload \
-H "X-App-ID: wx1234567890" \
-H "Authorization: Bearer a1b2c3..." \
-F "file=@/path/to/photo.jpg"
3.7 GET /api/ma/files/:id(小程序查询元数据)
同 3.3,返回 File 对象;不存在 40400。
3.8 GET /api/ma/files/:id/raw(小程序获取文件流)
同 3.4,返回文件流;不存在 40400。
3.9 DELETE /api/ma/files/:id(小程序删除)
同 3.5,删除文件内容与元数据;不存在 40400。
4. 数据模型
4.1 File(files 表)
| 字段 | 类型 | JSON | 说明 |
|---|---|---|---|
ID |
uint64 | id |
主键,自增。 |
StorageKey |
string | storage_key |
存储后端内的 key,形如 2026/10/27/<rand>.<ext>。 |
Filename |
string | filename |
落盘文件名(含扩展名)。 |
OriginName |
string | origin_name |
用户上传时的原始文件名。 |
MimeType |
string | mime_type |
由扩展名推断,未知回退 application/octet-stream。 |
Ext |
string | ext |
小写扩展名,不含点。 |
Size |
int64 | size |
文件字节数。 |
UploaderType |
string | uploader_type |
上传者类型:admin 或 ma。 |
UploaderID |
uint64 | uploader_id |
上传者主键(管理员 ID 或小程序用户 ID)。 |
URL |
string | url |
可访问地址(base_url 拼接 storage_key)。 |
CreatedAt |
time | created_at |
创建时间(Y-m-d H:i:s)。 |
GET .../raw直接返回二进制流,不返回FileJSON;GET .../:id返回上述File对象。 上传成功响应(§3.2)仅返回摘要字段:id/url/filename/origin_name/mime_type/size。
5. 错误码
| 业务码 | 常量 | 含义 |
|---|---|---|
0 |
— | 成功 |
40000 |
ErrBadRequest |
请求错误(如未选择文件 / ID 非法) |
40100 |
ErrUnauthorized |
未认证(上传接口要求已登录身份) |
40300 |
ErrForbidden |
禁止访问 |
40400 |
ErrNotFound |
文件不存在 |
41300 |
ErrFileTooLarge |
文件大小超过 max_size 限制 |
41500 |
ErrFileTypeForbid |
扩展名不在 allowed_ext 白名单 |
50000 |
ErrServer |
服务内部错误(存储 IO / 落库失败等) |
6. 备注
- 批量上传:当前仅支持单文件(
form字段file)。批量能力可后续在 handler 层遍历c.MultipartForm.File["file"]调用同一svc.Upload,Service 无需改动。 - 对象存储扩展:接入 minIO/S3 时新增
internal/pkg/storage/<backend>实现Storage接口,在storage.New增加对应case并在config.yaml设置upload.storage_type即可,接口与 Service 层保持不变。 - 安全:存储 key 经
filepath.Clean与根目录二次校验,杜绝目录穿越;文件名以随机值重命名,原始名仅作展示,避免路径注入。 - 静态访问公开性:本地模式返回的
url由r.Static公开服务。若文件需鉴权访问,应改走.../raw接口(受对应入口鉴权保护),或后续切换为对象存储签名 URL。