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

移动端上传XML时为什么容易因网络切换失败
因为移动设备在Wi-Fi和蜂窝网络间切换时,TCP连接大概率会重置,XMLHttpRequest 或 fetch 会直接抛出 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 进度事件丢失问题
uploadId 生命周期管理、分片幂等写入、本地断点存储这三个环节漏掉任一,都会导致重复上传或数据损坏。










