[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"i-ri:search-line":3,"i-ri:moon-line":8,"i-ri:sun-line":10,"i-ri:menu-3-line":12,"i-ri:close-line":14,"notes-module-items-im":16,"notes-page-im-03-message-content\u002F02-image-send":66,"i-ri:arrow-left-s-line":193,"i-ri:menu-2-line":195,"i-ri:arrow-right-s-line":197},{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":7},0,24,false,"\u003Cpath fill=\"currentColor\" d=\"m18.031 16.617l4.283 4.282l-1.415 1.415l-4.282-4.283A8.96 8.96 0 0 1 11 20c-4.968 0-9-4.032-9-9s4.032-9 9-9s9 4.032 9 9a8.96 8.96 0 0 1-1.969 5.617m-2.006-.742A6.98 6.98 0 0 0 18 11c0-3.867-3.133-7-7-7s-7 3.133-7 7s3.133 7 7 7a6.98 6.98 0 0 0 4.875-1.975z\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":9},"\u003Cpath fill=\"currentColor\" d=\"M10 7a7 7 0 0 0 12 4.9v.1c0 5.523-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2h.1A6.98 6.98 0 0 0 10 7m-6 5a8 8 0 0 0 15.062 3.762A9 9 0 0 1 8.238 4.938A8 8 0 0 0 4 12\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":11},"\u003Cpath fill=\"currentColor\" d=\"M12 18a6 6 0 1 1 0-12a6 6 0 0 1 0 12m0-2a4 4 0 1 0 0-8a4 4 0 0 0 0 8M11 1h2v3h-2zm0 19h2v3h-2zM3.515 4.929l1.414-1.414L7.05 5.636L5.636 7.05zM16.95 18.364l1.414-1.414l2.121 2.121l-1.414 1.414zm2.121-14.85l1.414 1.415l-2.121 2.121l-1.414-1.414zM5.636 16.95l1.414 1.414l-2.121 2.121l-1.414-1.414zM23 11v2h-3v-2zM4 11v2H1v-2z\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":13},"\u003Cpath fill=\"currentColor\" d=\"M3 4h18v2H3zm6 7h12v2H9zm-6 7h18v2H3z\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":15},"\u003Cpath fill=\"currentColor\" d=\"m12 10.587l4.95-4.95l1.414 1.414l-4.95 4.95l4.95 4.95l-1.415 1.414l-4.95-4.95l-4.949 4.95l-1.414-1.415l4.95-4.95l-4.95-4.95L7.05 5.638z\"\u002F>",[17,22,26,30,34,38,42,46,50,54,58,62],{"path":18,"title":19,"meta":20},"\u002Fnotes\u002Fim\u002F01-message-state\u002F01-offline-sync","连接恢复了，离线消息为什么还没补齐？",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},"2026-11-05T09:47:15.000Z",{"path":23,"title":24,"meta":25},"\u002Fnotes\u002Fim\u002F01-message-state\u002F02-read-cursor","手机读过了，电脑为何又显示未读？",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":27,"title":28,"meta":29},"\u002Fnotes\u002Fim\u002F01-message-state\u002F03-pagination-search","聊天搜索跳回旧消息后，怎样继续翻页？",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":31,"title":32,"meta":33},"\u002Fnotes\u002Fim\u002F01-message-state\u002F04-unread-refresh","消息没停，未读角标就不更新？",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":35,"title":36,"meta":37},"\u002Fnotes\u002Fim\u002F02-connection-protocol\u002F01-stream-parsing","一次接收回调，为什么不是一条消息？",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":39,"title":40,"meta":41},"\u002Fnotes\u002Fim\u002F02-connection-protocol\u002F02-request-response","长连接上的请求，怎样等到自己的响应？",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":43,"title":44,"meta":45},"\u002Fnotes\u002Fim\u002F02-connection-protocol\u002F03-connection-lifecycle","登录一直在连接中，到底在等什么？",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":47,"title":48,"meta":49},"\u002Fnotes\u002Fim\u002F03-message-content\u002F01-file-download","附件下载完了，为什么还是打不开？",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":51,"title":52,"meta":53},"\u002Fnotes\u002Fim\u002F03-message-content\u002F02-image-send","图片上传到 100%，为什么还没发出去？",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":55,"title":56,"meta":57},"\u002Fnotes\u002Fim\u002F03-message-content\u002F03-mentions","聊天 @提及怎样保存身份和位置？",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":59,"title":60,"meta":61},"\u002Fnotes\u002Fim\u002F04-client-release\u002F01-flutter-build","Release 包为什么仍连着测试环境？",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":63,"title":64,"meta":65},"\u002Fnotes\u002Fim","即时通信",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"id":67,"type":68,"sourcePath":69,"routePath":51,"path":51,"title":52,"description":70,"updatedAt":21,"meta":71,"body":79,"toc":185},"bm90ZTpub3Rlcy9pbS8wMy1tZXNzYWdlLWNvbnRlbnQvMDItaW1hZ2Utc2VuZC5tZA","note","notes\u002Fim\u002F03-message-content\u002F02-image-send.md","聊天里选一张图片，自己的窗口已经能看见，进度也走到 100%，对方却还没收到。这里至少有两次远端操作：上传文件，以及发送一条带文件引用的聊天消息。",{"id":67,"type":68,"sourcePath":69,"slug":72,"title":52,"updatedAt":21,"description":70,"tags":73,"date":76,"comment":77,"summary":78,"lastUpdated":21},"im\u002F03-message-content\u002F02-image-send",[74,75],"frontend","IM","2026-09-04",true,"本地预览、文件上传和聊天发送分别有结果。把三个阶段分开，才能判断该重传文件、重查消息，还是恢复接收端访问。",{"type":80,"children":81},"root",[82,90,95,100,107,112,117,122,128,133,138,143,149,154,159,164,170,175,180],{"type":83,"tag":84,"props":85,"children":87},"element","h1",{"id":86},"图片上传到-100为什么还没发出去",[88],{"type":89,"value":52},"text",{"type":83,"tag":91,"props":92,"children":93},"p",{},[94],{"type":89,"value":70},{"type":83,"tag":91,"props":96,"children":97},{},[98],{"type":89,"value":99},"项目的 Web 工作台把图片交给文件服务，再通过 XMPP 发出文件名、引用、类型和大小。浏览器里的 File 或 Blob 还可以直接用于本地预览，因此“我看得见”也可能发生在上传之前。",{"type":83,"tag":101,"props":102,"children":104},"h2",{"id":103},"一个发送中状态为什么不够用",[105],{"type":89,"value":106},"一个发送中状态，为什么不够用？",{"type":83,"tag":91,"props":108,"children":109},{},[110],{"type":89,"value":111},"可以把整条流程显示成一个忙碌状态，但失败后就很难说明该从哪里恢复。拆开本地准备、文件上传和消息确认，才能保留各阶段已经取得的结果。",{"type":83,"tag":91,"props":113,"children":114},{},[115],{"type":89,"value":116},"上传进度报告的是传输过程，100% 后还要等上传调用返回，确认拿到了可用引用；引用拿到以后，才构造聊天消息，再等待消息确认。接收端能否下载，又是之后的文件访问结果。",{"type":83,"tag":91,"props":118,"children":119},{},[120],{"type":89,"value":121},"项目因此更新上传百分比时不直接把消息标为已发送。百分比消失、消息仍在等待，是可能出现的中间状态，界面需要能解释它现在是在上传还是等确认。",{"type":83,"tag":101,"props":123,"children":125},{"id":124},"用户选完图消息为什么还没出现在列表",[126],{"type":89,"value":127},"用户选完图，消息为什么还没出现在列表？",{"type":83,"tag":91,"props":129,"children":130},{},[131],{"type":89,"value":132},"为了立即反馈，可以先创建占位消息，再在后台准备预览。项目当前的顺序则是先准备文件名、按条件生成缩略图，再创建本地 SENDING 消息。",{"type":83,"tag":91,"props":134,"children":135},{},[136],{"type":89,"value":137},"这意味着选图后还可能停在前置处理，上传根本没开始。缩略图生成又位于后面主异常处理之外，前置失败未必能更新到同一条消息上，这部分反馈与重试入口仍需继续检查。",{"type":83,"tag":91,"props":139,"children":140},{},[141],{"type":89,"value":142},"原始 File 会留在内存映射中；有缩略图时，还尝试把缩略图和原文件写入 Blob 存储。缓存失败会记录警告，后面仍继续发送，它不应直接等同于远端上传失败。",{"type":83,"tag":101,"props":144,"children":146},{"id":145},"上传成功消息失败重试还要再上传吗",[147],{"type":89,"value":148},"上传成功、消息失败，重试还要再上传吗？",{"type":83,"tag":91,"props":150,"children":151},{},[152],{"type":89,"value":153},"如果文件引用仍可用，可以复用它，只恢复后续消息发送。否则重复上传既浪费流量，也可能留下更多远端对象。",{"type":83,"tag":91,"props":155,"children":156},{},[157],{"type":89,"value":158},"项目根据是否已有非空 URL 决定要不要上传。新上传路径拿到引用后写回消息，再检查连接、构造协议消息并等待确认；已有 URL 则跳过上传。",{"type":83,"tag":91,"props":160,"children":161},{},[162],{"type":89,"value":163},"但跳过上传不会自动保持消息身份。重试仍要对应原消息、发送 ID 与确认目标，避免迟到结果改到另一条记录。当前也只检查 URL 字符串，没有验证对象是否仍存在或引用是否过期，失效后如何恢复还取决于文件服务。",{"type":83,"tag":101,"props":165,"children":167},{"id":166},"失败后应该让用户重试哪一步",[168],{"type":89,"value":169},"失败后，应该让用户重试哪一步？",{"type":83,"tag":91,"props":171,"children":172},{},[173],{"type":89,"value":174},"本地准备失败时，消息可能尚未创建；上传失败时，需要保留原始文件；上传已成功但消息未确认时，应保留远端引用并先核对旧消息。消息已确认、只是接收端下载失败，则应恢复文件访问，而不是直接再发一条消息。",{"type":83,"tag":91,"props":176,"children":177},{},[178],{"type":89,"value":179},"项目当前的主异常收尾还受“本轮是否需要上传”控制，已有 URL 的分支在确认前抛错时，未必与新上传分支同样更新失败状态。这是需要沿分支继续检查的地方。",{"type":83,"tag":91,"props":181,"children":182},{},[183],{"type":89,"value":184},"如果文件已上传，消息最终没发成，远端也可能留下无关联文件，是否回收不能只从客户端确定。发送端可能一直在看本地缓存，要换一台设备访问远端引用才能检查接收端路径；刷新后预览消失，也不直接说明消息没发送成功。缓存 key 找预览，文件引用找内容，消息 ID 找发送结果，三者各自核对。",{"title":186,"searchDepth":187,"depth":187,"links":188},"",2,[189,190,191,192],{"id":103,"depth":187,"text":106},{"id":124,"depth":187,"text":127},{"id":145,"depth":187,"text":148},{"id":166,"depth":187,"text":169},{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":194},"\u003Cpath fill=\"currentColor\" d=\"m10.828 12l4.95 4.95l-1.414 1.415L8 12l6.364-6.364l1.414 1.414z\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":196},"\u003Cpath fill=\"currentColor\" d=\"M3 4h18v2H3zm0 7h12v2H3zm0 7h18v2H3z\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":198},"\u003Cpath fill=\"currentColor\" d=\"m13.172 12l-4.95-4.95l1.414-1.413L16 12l-6.364 6.364l-1.414-1.415z\"\u002F>"]