Files
2026-10-27 22:55:36 +08:00

8.6 KiB
Raw Permalink Blame History

文件上传服务接口文档

本文件描述文件上传服务的全部接口,包含**后台管理(/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 直接返回二进制流,不返回 File JSON;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。