做移动端 AI 图片识别时踩了个坑:从相册选图上传一切正常,换成直接拍照就必现失败,接口报 Network Error 或 Abort。
折腾了好一阵才把根因理清。把排查过程整理下来,顺便聊聊 WebView、SSE、File API 在 Hybrid 里的保活和文件处理。
一、现象
相册选图正常,相机拍摄后页面闪退或请求中断。
从相册选图时,用户 stays 在当前 H5 页,只是短暂切出去选图再回来,连接一直在线。直接拍照不同——会跳转到系统相机 App,当前页相当于进入后台,document.hidden 变为 true。
二、根因定位
连接层:后台 SSE 长连接因 WebView 内存回收机制被动切断
H5 页面与服务器通过 Server-Sent Events (SSE) 维持长连接。当用户调起相机时,WebView 进程可能被系统回收或置于低优先级,不少 SSE 库的默认策略是:页面不可见(document.hidden === true)时关闭连接以节省资源。
于是等你拍完照回到 H5,连接早已被关掉,依赖该连接的上传请求自然失败。
解决方案:明确告诉 SSE 库,页面暂时不可见时不要关闭连接。例如配置 openWhenHidden: true。同时实现 SSE 断线重连与状态保活机制,确保从相机返回后连接仍有效。
数据层:自定义组件库对 File 对象的封装未兼容 file.raw 原始流,导致序列化丢失
另一头的问题是:我们想上传的是真实的文件流(Blob),但像 Element Plus Upload 这类组件会对文件做一层封装(Proxy 或包装对象)。如果直接把组件给的那个对象塞进 FormData,服务端收到的可能变成 [object Object] 之类的字符串,而不是真正的二进制数据。
所以上传前要"解包":不管是组件直接给的 file 还是包装过的,都统一取真正的文件实例再放进 FormData,例如使用 file.raw(视具体组件 API 而定),保证寄出去的是文件本身,确保二进制流在上传链路中的完整性。
三、解决方案
一是做 SSE 断线重连和状态保活;二是重构文件处理逻辑,上传前对 file 做 raw 解包,确保 Blob 完整。
修完之后,iOS 和 Android 上拍照再上传都稳了。问题本身不复杂,主要是被"相册正常、拍照必挂"的现象带偏,一度以为是相机或机型兼容,其实还是页面生命周期和数据结构这两块。