This commit is contained in:
Your Name
2026-09-03 08:38:17 +08:00
parent 6cd4f1b1db
commit 842990b0e7
1853 changed files with 278406 additions and 361 deletions
@@ -0,0 +1,51 @@
# 头像上传与缩略图
头像专用上传在服务端生成静态图片,不需要开通 COS、OSS 等厂商的图片处理功能,也不改变管理员选择的存储厂商。当前规则只应用于新上传的头像,不下载、覆盖或批量替换已有用户头像。
## 接口
`POST /api/v1/media/upload`,使用原有用户认证,`multipart/form-data`
- `file`:头像文件。
- `purpose=avatar`:启用头像处理。不传此字段时,普通图片、封面、语音和证据上传仍原样保存。
成功响应的 `data` 保留已有字段,同时提供两个可选地址:
```json
{
"id": 42,
"name": "42-1788189400000-av1.jpg",
"url": "https://cdn.example.com/media/2026/08/42-1788189400000-av1.jpg",
"thumbnailUrl": "https://cdn.example.com/media/2026/08/42-1788189400000-av1-thumb.jpg",
"originalUrl": "https://cdn.example.com/media/2026/08/42-1788189400000-av1-original.png",
"provider": "tencent_cos",
"objectKey": "media/2026/08/42-1788189400000-av1.jpg"
}
```
- `url`:最长边不超过 **640px** 的 JPEG 展示图,质量 82。将此 URL 保存到用户资料的 `avatar` 字段。旧客户端和管理端也能正常显示这张较小的图片。
- `thumbnailUrl`:最长边不超过 **256px** 的 JPEG 缩略图,质量 78。uni-app 的列表、聊天、评论、资料头像等小尺寸位置加载此图;首页推荐卡片使用 640px 展示图。
- `originalUrl`:本次上传的原始文件副本,字节不变;若客户端选择图片时已压缩,则这里保留的是压缩后实际上传的文件,并非相册原始文件。
- 未传 `purpose` 的普通媒体上传,`thumbnailUrl``originalUrl` 为空字符串,`url` 仍为原文件。
图片等比例缩小,不放大、不在服务器裁剪人脸。客户端继续通过 `aspectFill` 显示。JPEG 手机照片先按 EXIF 方向校正,再缩放;展示图和缩略图不含原 EXIF/GPS。透明区域合成白底。GIF 使用首帧,WebP 使用解码器支持的静态图片;不支持的动画 WebP 或损坏文件返回明确错误,不悄悄退回高清原图。
## 可靠性与兼容性
- 延用 16 MiB 文件限制,头像另限制为最多 2400 万像素、单边最多 16384px,并限制同时解码的数量,避免小服务器被大图耗尽内存。处理繁忙时返回 503 和 `Retry-After: 2`
- 原图、缩略图和展示图全部上传成功后,才激活媒体记录、返回成功。中途失败或数据库收尾失败会尝试删除已写入的对象和未完成的记录;清理使用独立超时,不受客户端取消影响。本地同名文件不会被覆盖或误删。
- `media_assets` 记录主展示图及其实际 MIME/大小,不需要数据库迁移;原图和缩略图使用同一前缀的配套文件。以后实现物理删除或存储生命周期规则时,需要同时处理三种文件。
- `-av1` 文件名是已生成配套缩略图的标识。客户端只改写这些普通公开 URL,旧头像、第三方 OAuth 头像、临时本地文件和带查询参数的签名 URL 保持原样。缩略图失败时仅回退一次到展示图,防止重复请求。
- 头像组件附带 `lazy-load`,由支持此属性的平台延迟加载;主要性能收益来自真正减小图片像素和文件体积,不依赖所有端都支持懒加载。
- 编辑资料时禁止上传未结束就保存,避免把旧头像误存回服务器。令牌刷新重试会保留 `purpose=avatar`
- 旧客户端继续调用普通上传接口时行为不变。需要新版客户端的头像上传调用,才会触发生成;已存在的头像不会自动变小,需要重新上传或另行安排有备份的批量转换。
## 验证与发布
运行 `go test ./...``pnpm test:avatars``pnpm test:native-components``pnpm type-check`。回归覆盖尺寸、透明图、方向信息、GIF/WebP、损坏或超大图片、存储失败清理、本地同名文件保护、实际 HTTP 上传/读取,以及前端回退和令牌刷新。
项目里的一个头像样本从 1,853,171 字节生成 46,180 字节展示图与 11,131 字节缩略图,缩略图缩小约 99.4%;比例取决于原图内容和格式,不能视作所有图片的固定压缩率。
上线需要更新后端和 H5,原生 App 需重新构建/安装。这份改动本身不执行生产部署,不改线上账号或历史文件。旧后端不认识新增的本地 `-av1` 文件名;如果使用本地存储,回滚服务时必须保留新的静态文件名规则,或由静态服务器继续提供这些文件。
实现参考:[imaging 缩放和 EXIF 方向处理](https://github.com/disintegration/imaging)、[uni-app image 平台属性](https://uniapp.dcloud.net.cn/component/image.html)。