8.0 KiB
聊天图片、文件消息接入
适用:企业与求职者的 DIRECT 会话。本文对应本批后端候选源码;尚未提交、推送或部署,线上 #94 还没有这些新接口。联调前确认本批已部署。
基础路径沿用项目 API_BASE_URL /api/v1。全部接口均要求 Authorization: Bearer <accessToken>,不能把 Token 放在 URL。现有文字、简历卡片接口保持原用法。普通 Emoji 仍走文字消息。
1. 上传并发送
POST /conversations/{id}/attachment-messages
请求类型 multipart/form-data,字段:
| 字段 | 必填 | 内容 |
|---|---|---|
file |
是 | 一个文件;文件名和 MIME 必须正确 |
kind |
是 | IMAGE 或 FILE,大写 |
clientMessageId |
是 | 客户端生成,1–64 位字母、数字、_、- |
调用一次即上传并发送一条消息,没有单独“先上传再发 attachmentId”的步骤。使用项目现有鉴权请求封装;传 FormData 时不要手动写缺少 boundary 的 Content-Type。
成功:HTTP 200,沿用 {code:200,data:MessageResponse,requestId:...} 信封。IMAGE 和 FILE 消息都在既有 attachment 字段返回元数据。例如(示意数据):
{
"id": 123,
"clientMessageId": "image_abc123",
"conversationId": 10,
"senderUserId": 62,
"messageType": "IMAGE",
"content": "[图片]",
"status": "SENT",
"version": 1,
"attachment": {
"fileName": "office.png",
"contentType": "image/png",
"size": 18420,
"sha256": "服务端实际保存图片的SHA256",
"width": 1280,
"height": 720,
"available": true,
"downloadPath": "/conversations/10/messages/123/attachment",
"previewPath": "/conversations/10/messages/123/attachment/preview",
"thumbnailPath": "/conversations/10/messages/123/attachment/thumbnail"
}
}
FILE 的 previewPath、thumbnailPath、width、height 为空或省略。以上路径均相对 /api/v1,不是公开下载链接。不要自行拼接本地文件路径或对象存储地址。
文件范围
- 图片:JPEG(
.jpg/.jpeg)与 PNG;实际解码校验,最多 1600 万像素、任一边长不超过 8192。首版不支持 GIF、WebP、HEIC、SVG,前端选取时过滤或在明确告知后转换为支持格式。 - 普通文件:PDF、DOC、DOCX、UTF-8 TXT;不支持 XLS/XLSX、ZIP、安装包或可执行文件。
- 正确 MIME 分别为
image/jpeg、image/png、application/pdf、application/msword、application/vnd.openxmlformats-officedocument.wordprocessingml.document、text/plain。 - 单文件最多 10 MiB(10,485,760 字节),空文件拒绝。会话有效附件默认累计最多 100 MiB。缩略图最长边 320,JPEG 格式。
- 图片重新编码以移除 EXIF 等附加元数据,
size/sha256对应保存后的图片,可能与客户端原文件不同。 - 文档执行扩展名、MIME、签名及 DOCX 压缩包结构检查;这不等于病毒扫描。
重试与错误
发送前生成 clientMessageId,网络超时后重试必须使用相同会话、账号、文件字节、文件名、MIME、kind 和 clientMessageId。相同请求返回原消息,不重复入库或推送;相同 key 改文件、改类型或与文字/简历消息混用返回 400。不要为一次自动重试重新生成 key。
- HTTP 400:缺字段、非法类型、伪造内容、尺寸/业务大小超限、重复 key 内容不一致或会话配额耗尽;显示后端错误并保留重试/重新选择入口。
- HTTP 413:Servlet 层拦截超大 multipart,请提示文件过大。
- HTTP 401:未登录、会话失效或账号禁用。
- HTTP 403:非成员、屏蔽、企业未认证、当前无有效投递沟通权限,或不是 DIRECT 会话。
- HTTP 404:会话/消息不存在、附件已撤回/清除或发送者账号已禁用。
失败不能显示“已发送”。上传进度是否展示由前端传输层实现;当前后端不提供断点续传、分片或服务器进度接口。
2. 缩略图、预览和下载
| 接口 | 返回 |
|---|---|
GET /conversations/{conversationId}/messages/{messageId}/attachment |
附件原始保存内容,Content-Disposition 为 attachment |
GET .../attachment/preview |
仅 IMAGE,重新编码的全尺寸图片,inline |
GET .../attachment/thumbnail |
仅 IMAGE,JPEG 缩略图,inline |
均返回二进制,不经过 JSON 解包;响应头包括真实 Content-Type、Content-Length、Cache-Control: no-store 和 X-Content-Type-Options: nosniff。
列表优先加载 thumbnail,点击后鉴权加载 preview;文件消息显示 fileName、size 和下载操作。图片请求也要带 Authorization,不能只把受保护地址直接丢给不带鉴权的图片组件。
若先下载到 App 私有缓存再预览,应按账号隔离,退出账号、切换账号、撤回和失效时清除相关缓存;不要复制到公共相册或公共目录后仍宣称能被服务端撤回。已下载到设备或截图的内容,服务端不能远程收回。
每次下载重新检查会话成员、企业认证、有效沟通投递、屏蔽、消息状态和发送者有效性。列表里的 available:true 只代表列表读取时附件尚存,不保证之后每次请求仍获授权。
3. 消息列表、实时事件、撤回
现有 GET /conversations/{id}/messages 及 GET /conversations/{id}/events 会返回同样的 MessageResponse/attachment。WebSocket STOMP 的 MESSAGE_CREATED、MESSAGE_RECALLED 事件也使用同一结构。
- 上传成功响应和实时事件可能都到达:按
message.id + version合并,不能直接 append 两次。 - 重连后使用既有游标补拉,补拉成功合并后才推进 nextCursor;不要用实时事件 eventId 直接覆盖补拉游标。
IMAGE/FILE走对应气泡;保留 TEXT 和 RESUME 的现有渲染。- 撤回沿用
POST /conversations/{conversationId}/messages/{messageId}/recall:仅本人,沿用两分钟窗口。 - 撤回成功后删除服务端原件/缩略图,消息保留“消息已撤回”,attachment.available=false,地址与元数据清空,前端停止展示/加载旧图。
4. 现有前端修改位置
app/chat/[id].tsx:把图片/文件按钮的占位提示替换为选择器;新增图片气泡、文件卡片、预览及下载反馈。src/types/message.ts:扩展消息类型与 attachment 元数据,兼容无 attachment 的旧消息。src/api/api.ts:multipart 发送、鉴权二进制请求与错误转换;所有请求沿用当前登录态。src/hooks/useConversation.ts:发送状态、稳定 clientMessageId、重试、按 ID/版本合并、实时事件与重连补拉。
页面只处理展示和交互,请求与状态编排仍放在 API/Hook 层。地图功能不在本批范围。
5. 联调验收
- 企业、求职者在已允许沟通的投递内互发 PNG/JPEG/TXT/PDF,双方当前聊天页立即出现且不重复。
- 缩略图、全图、文件名/大小正确,下载字节与后端 sha256 一致;图片按服务端重新编码后的摘要核对。
- 超时重试复用 key,只产生一条;换文件复用同 key 被拒绝。
- 切账号、陌生账号、另一会话 ID、屏蔽、撤回、禁用账号无法继续读取附件。
- 接收方断网后重连补拉,退出重进及后端重启后历史附件仍在。
- 空文件、超限文件、伪装 PNG、非法文档和不支持格式均明确失败,页面无假成功或白屏。
6. 后端部署前提
需迁移 V62(新表 chat_attachments 与消息元数据列)。二进制在数据库中和消息一起提交,无需开放公开文件目录。MySQL max_allowed_packet 应至少 16 MiB,代理上传限制应允许约 11 MiB 的 multipart 请求;业务单文件仍受 10 MiB 限制。生产部署、代理配置修改需单独授权。
Servlet 上传边界至少为单文件 10 MiB、请求 11 MiB;既有更大的配置予以保留。会话业务配额可配置 app.chat.attachments.max-conversation-bytes(默认 104857600)。首次版本采用限额与撤回释放空间,尚无长期附件管理/任意时间删除接口。