从移动端上传XML时如何处理网络切换和中断

2次阅读

移动端xml上传易因网络切换失败,因TCP连接重置导致请求中断且无断点续传;需用Blob分片、uploadId、本地存储及服务端校验实现可恢复上传。

从移动端上传XML时如何处理网络切换和中断

移动端上传XML时为什么容易因网络切换失败

因为移动设备在Wi-Fi和蜂窝网络间切换时,TCP连接大概率会重置,XMLHttpRequestfetch 会直接抛出 NetworkError 或进入 aborted 状态,而原生上传不带断点续传能力,整个 XML 文件得从头重发。

用 Blob 分片 + 唯一请求ID实现可恢复上传

核心是把大 XML 拆成固定大小的 Blob 片段(比如 512KB),每片单独上传,并附带当前分片序号、总片数、文件哈希和客户端生成的 uploadId。服务端按 uploadId 聚合所有成功接收的分片,最后拼接并校验完整 XML。

  • 前端需监听 navigator.onLine 变化,在离线时暂停上传队列,切回在线后自动重试未完成分片
  • 每个分片请求必须设 timeout(建议 15–30s),避免卡死在弱网中
  • 使用 AbortController 控制单个分片请求生命周期,网络切换时主动 abort() 并标记该片待重试
  • XML 内容若含敏感字段,应在分片前用 TextEncoder 转为 Uint8Array,再加密(如 AES-GCM)——服务端解密后才拼接,避免中间态明文泄露
const uploadChunk = async (blob, index, total, uploadId, signal) => {   const formData = new FormData();   formData.append('chunk', blob);   formData.append('index', index);   formData.append('total', total);   formData.append('uploadId', uploadId);      const res = await fetch('/api/upload-xml-chunk', {     method: 'POST',     body: formData,     signal   });      if (!res.ok) throw new Error(`Chunk ${index} failed: ${res.status}`);   return res.json(); };

服务端必须校验分片完整性与顺序

仅靠前端传的 index 不可靠——可能乱序到达或重复提交。服务端要:对每个 uploadId 维护一个 redis Hash,键为 upload:${uploadId}:chunks,字段是 index,值为该分片 SHA-256;收到新分片时先比对哈希,一致才写入;合并前检查是否收齐 0..total-1 且无缺失。

  • XML 根节点可能被切在分片边界,所以不能直接解析单个分片——必须等全部拼完再用 DOMParser 或流式 XML 解析器(如 sax)处理
  • 单次上传超时后,前端应等待至少 2 秒再重试同一分片,避免服务端被高频重试压垮
  • 若用户中途退出页面,需在 beforeunload 中将当前 uploadId 和已传分片索引存入 localStorage,下次进入时读取并继续

ios safari 的额外限制要绕过

iOS 15+ 对后台标签页的 fetch 有严格节流,即使页面在前台但锁屏后,上传也可能被系统挂起。不能依赖 setTimeout 轮询状态,得用 background Fetch API(兼容性差)或更稳妥的方式:

  • 上传前调用 navigator.locks.request() 占用一个锁,防止多实例并发冲突
  • 对 iOS 设备,强制将 XML 转为 ArrayBuffer 后用 XMLHttpRequest 替代 fetch,因其对中断回调更稳定
  • 禁用 XMLHttpRequest.upload.onprogress,改用服务端返回的进度字段(如 { uploadedChunks: 3, totalChunks: 7 }),规避 Safari 进度事件丢失问题

XML 文件本身没有特殊传输语义,真正复杂的是移动端不可靠网络下的状态同步——uploadId 生命周期管理、分片幂等写入、本地断点存储这三个环节漏掉任一,都会导致重复上传或数据损坏。

text=ZqhQzanResources