Files
kefu/im/backend/docs/avatar-thumbnails.md
T
2026-09-03 08:38:17 +08:00

5.5 KiB
Raw Blame History

头像上传与缩略图

头像专用上传在服务端生成静态图片,不需要开通 COS、OSS 等厂商的图片处理功能,也不改变管理员选择的存储厂商。当前规则只应用于新上传的头像,不下载、覆盖或批量替换已有用户头像。

接口

POST /api/v1/media/upload,使用原有用户认证,multipart/form-data

  • file:头像文件。
  • purpose=avatar:启用头像处理。不传此字段时,普通图片、封面、语音和证据上传仍原样保存。

成功响应的 data 保留已有字段,同时提供两个可选地址:

{
  "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 中首页网格、附近列表、搜索、关注/粉丝、消息、聊天、动态作者、评论、资料页、编辑资料、会员页等所有头像展示统一加载此图;普通动态图片的预览仍使用展示图或原图。
  • originalUrl:本次上传的原始文件副本,字节不变;若客户端选择图片时已压缩,则这里保留的是压缩后实际上传的文件,并非相册原始文件。
  • 未传 purpose 的普通图片上传会保留 url 原文件,并生成 thumbnailUrloriginalUrl 为空。语音上传不生成图片缩略图。

图片等比例缩小,不放大、不在服务器裁剪人脸。客户端继续通过 aspectFill 显示。JPEG 手机照片先按 EXIF 方向校正,再缩放;展示图和缩略图不含原 EXIF/GPS。透明区域合成白底。GIF 使用首帧,WebP 使用解码器支持的静态图片;不支持的动画 WebP 或损坏文件返回明确错误,不悄悄退回高清原图。

可靠性与兼容性

  • 延用 16 MiB 文件限制,头像另限制为最多 2400 万像素、单边最多 16384px,并限制同时解码的数量,避免小服务器被大图耗尽内存。处理繁忙时返回 503 和 Retry-After: 2
  • 原图、缩略图和展示图全部上传成功后,才激活媒体记录、返回成功。中途失败或数据库收尾失败会尝试删除已写入的对象和未完成的记录;清理使用独立超时,不受客户端取消影响。本地同名文件不会被覆盖或误删。
  • media_assets 记录主展示图及其实际 MIME/大小,不需要数据库迁移;原图和缩略图使用同一前缀的配套文件。以后实现物理删除或存储生命周期规则时,需要同时处理三种文件。
  • -av1 是头像配套缩略图标识,-im1 是普通图片配套缩略图标识。客户端只改写这些普通公开 URL,旧图片、第三方 OAuth 头像、临时本地文件和带查询参数的签名 URL 保持原样。缩略图失败时仅回退一次到展示图或原图,防止重复请求。
  • uni-app 的 AvatarImage 不再提供展示图模式,所有头像入口必须经过 AvatarImageUserAvatar。回归测试会扫描全部 Vue 页面,禁止原生 <image> 直载头像、头像背景 URL 和展示图模式,避免后续页面重新引入大头像。
  • 测试用户头像迁移工具会为十张公共测试头像同时发布 256px JPEG 配套对象;列表接口通过 avatarThumbnail 返回该地址,avatar 仍保留原图供大图场景使用。
  • 头像组件附带 lazy-load,由支持此属性的平台延迟加载;主要性能收益来自真正减小图片像素和文件体积,不依赖所有端都支持懒加载。
  • 编辑资料时禁止上传未结束就保存,避免把旧头像误存回服务器。令牌刷新重试会保留 purpose=avatar
  • 旧客户端继续调用普通上传接口时行为不变。需要新版客户端的头像上传调用,才会触发生成;已存在的头像不会自动变小,需要重新上传或另行安排有备份的批量转换。

验证与发布

运行 go test ./...pnpm test:avatarspnpm test:native-componentspnpm type-check。回归覆盖尺寸、透明图、方向信息、GIF/WebP、损坏或超大图片、存储失败清理、本地同名文件保护、实际 HTTP 上传/读取,以及前端回退和令牌刷新。

项目里的一个头像样本从 1,853,171 字节生成 46,180 字节展示图与 11,131 字节缩略图,缩略图缩小约 99.4%;比例取决于原图内容和格式,不能视作所有图片的固定压缩率。

2026-08-31 18:00 已部署后端及 H52026-09-01 已补齐测试用户 COS 缩略图,并在本地完成全局头像缩略图规则的 App/H5 构建。原生 App 仍需重新同步或打包安装。本次没有改线上账号或历史文件,详情见 deploy/im.bchongw.com_部署记录.md 的头像缩略图发布记录。旧后端不认识新增的本地 -av1 文件名;如果使用本地存储,回滚服务时必须保留新的静态文件名规则,或由静态服务器继续提供这些文件。

实现参考:imaging 缩放和 EXIF 方向处理uni-app image 平台属性