Skip to content

图片、视频等资源存放 ​

目标 ​

本文档说明当前代码中已经实现的头像、图片、视频资源处理规则。

当前实现分为两类:

  • 用户头像:注册时直接传 avatar_base64,服务端校验后保存到 users.avatar_url
  • 聊天资源:图片、视频通过上传接口保存到 static 目录,并返回 /api/static/... 访问地址

代码落点 ​

  • 注册接口:controllers/auth_controller.go
  • 用户创建:models/user_model.go
  • 头像 base64 校验:models/resource_model.go
  • 资源上传接口:controllers/resource_controller.go
  • 资源保存规则:models/resource_model.go
  • 路由注册:routers/router.go
  • 静态资源暴露:main.go

数据库要求 ​

头像 base64 可能超过 255 字符,因此 users.avatar_url 必须使用 LONGTEXT。

sql
ALTER TABLE users MODIFY avatar_url LONGTEXT NULL;

当前本地数据库已按以上规则调整。

用户头像 ​

注册接口 ​

text
POST /api/auth/register

请求类型:

text
application/json

请求体:

json
{
  "username": "alice",
  "password": "123456",
  "nickname": "Alice",
  "avatar_base64": "data:image/png;base64,iVBORw0KGgo="
}

字段说明:

  • username:必填,本地登录账号
  • password:必填,本地登录密码
  • nickname:选填,不传时默认使用 username
  • avatar_base64:选填,头像 base64 字符串;解码后的图片大小不能超过 10 MiB

avatar_base64 支持两种格式:

text
iVBORw0KGgo=
text
data:image/png;base64,iVBORw0KGgo=

服务端处理规则:

  • 为空时不保存头像,users.avatar_url 写入 NULL
  • 不为空时会校验 base64 是否可解码
  • 解码后的头像原始内容不能超过 10 MiB
  • 支持标准 base64 和 raw base64
  • 如果传入 data URL,服务端校验逗号后面的 base64 内容
  • 数据库存储原始传入字符串,方便前端直接展示

成功响应中的用户信息仍使用 avatar_url 字段:

json
{
  "code": 200,
  "data": {
    "user": {
      "id": 1,
      "username": "alice",
      "nickname": "Alice",
      "avatar_url": "data:image/png;base64,iVBORw0KGgo=",
      "auth_source": "local",
      "status": 1
    },
    "token": "<jwt-token>"
  },
  "message": "注册成功"
}

常见错误:

  • 400:参数校验失败
  • 400:头像 base64 不合法
  • 413:头像大小超过 10 MiB,或注册请求体过大
  • 409:用户名已存在
  • 500:服务器内部错误

图片和视频上传 ​

上传接口 ​

text
POST /api/resources/upload

请求要求:

  • 需要登录
  • 请求头携带 Authorization: Bearer <token>
  • 请求类型为 multipart/form-data
  • 单次上传请求体最大 100 MiB
  • 服务端同时校验扩展名白名单和图片/视频常见格式文件头,扩展名与内容不匹配时拒绝上传

表单字段:

  • file:必填,上传文件
  • type:必填,资源类型,只支持 image 或 video

保存路径 ​

图片保存到:

text
static/images/{user_id}/{timestamp}.{ext}

视频保存到:

text
static/videos/{user_id}/{timestamp}.{ext}

说明:

  • {user_id} 来自当前登录用户 ID
  • {timestamp} 当前实现使用纳秒级时间戳
  • {ext} 来自原始文件名后缀,并统一转为小写
  • 返回 URL 使用 /api/static/...
  • main.go 已通过 engine.Static("/api/static", "./static") 暴露静态资源

示例:

text
static/images/1/1780000000000000000.png
static/videos/1/1780000000000000000.mp4

对应访问地址:

text
/api/static/images/1/1780000000000000000.png
/api/static/videos/1/1780000000000000000.mp4

支持后缀 ​

图片:

  • .jpg
  • .jpeg
  • .png
  • .gif
  • .webp

视频:

  • .mp4
  • .mov
  • .webm

当前实现按文件后缀校验,不做内容 MIME 嗅探。

curl 示例 ​

上传图片:

bash
curl -X POST "http://127.0.0.1:8080/api/resources/upload" \
  -H "Authorization: Bearer <jwt-token>" \
  -F "type=image" \
  -F "file=@/path/to/demo.png"

上传视频:

bash
curl -X POST "http://127.0.0.1:8080/api/resources/upload" \
  -H "Authorization: Bearer <jwt-token>" \
  -F "type=video" \
  -F "file=@/path/to/demo.mp4"

成功响应 ​

图片响应示例:

json
{
  "code": 200,
  "data": {
    "type": "image",
    "url": "/api/static/images/1/1780000000000000000.png"
  },
  "message": "上传资源成功"
}

视频响应示例:

json
{
  "code": 200,
  "data": {
    "type": "video",
    "url": "/api/static/videos/1/1780000000000000000.mp4"
  },
  "message": "上传资源成功"
}

常见错误 ​

  • 400:未上传 file
  • 400:type 不是 image 或 video
  • 400:文件没有后缀
  • 400:文件后缀不在白名单中
  • 401:未登录或登录已失效
  • 500:创建目录、创建文件或保存文件失败

和消息接口的关系 ​

资源上传接口只负责保存文件并返回 URL,不会自动发送消息。

客户端发送图片或视频消息时,推荐流程:

  1. 调用 POST /api/resources/upload 上传图片或视频
  2. 拿到响应中的 url
  3. 调用消息发送接口,把 url 写入消息 content

图片消息示例:

json
{
  "conversation_id": 1,
  "client_msg_id": "client-image-001",
  "message_type": "image",
  "content": {
    "url": "/api/static/images/1/1780000000000000000.png"
  }
}

视频消息当前可以先按文件类资源发送,后续如需独立 video 消息类型,需要扩展 messages.message_type 枚举和消息内容校验规则。

当前边界 ​

  • 当前不生成缩略图
  • 当前不压缩图片或视频
  • 当前不做 MIME 内容嗅探
  • 当前不做对象存储上传
  • 当前不记录资源表
  • 当前消息接口不会自动清理 static 下的资源文件
  • 当前同名风险通过纳秒级时间戳降低,不额外生成随机后缀

基于 MIT 许可发布