低带宽下progress回调卡死主因是xmlhttprequest.upload.onprogress事件触发骤降,非layui bug;需用分片上传+服务端进度轮询(间隔≥500ms、超2s未更新才查)+干净响应头(禁gzip、关输出缓冲、配cors头)协同解决。
为什么低带宽下 progress 回调会卡死
不是 layui 本身 bug,而是浏览器 xmlhttprequest.upload.onprogress 在极低带宽(如 100kb/s 以下)或高延迟网络中触发频率骤降,甚至长时间不触发。layui 的 progress 回调完全依赖这个原生事件,一旦事件“断流”,进度条就停在某个值不动,用户误以为卡死。
用分片上传 + 主动轮询补足进度缺口
单纯等 onprogress 不可靠,必须叠加服务端状态反馈。关键不是禁用分片,而是让每片上传后立刻查服务端已接收进度:
- 前端每次上传一个分片(如
chunkSize: 1024 * 1024),附带唯一uploadId和当前chunkIndex - 上传成功后,立即发起 GET 请求:
/api/upload/progress?uploadId=xxx - 后端返回 JSON:
{"uploadedChunks": 3, "totalChunks": 12},前端据此重算百分比 - 避免轮询过频:两次查询间隔不少于 500ms,且仅当
progress超过 2s 未更新时才触发
before 和 done 里要做的三件事
低带宽下,上传前准备和上传后确认比平时更重要:
-
before中必须做:校验文件大小(obj.files[0].size)、生成uploadId(建议用file.name + file.lastModified拼接哈希)、预计算分片总数(Math.ceil(file.size / chunkSize)) -
done中不能只看res.code === 0:还要检查res.chunkIndex是否匹配预期,防止服务端漏写某片却返回成功 - 必须加
error回调:捕获网络中断、超时等异常,手动触发重试逻辑(uploadNextChunk()),而不是等 Layui 默认失败提示
服务端响应头容易被忽略的细节
低带宽环境对响应头更敏感,稍有不慎就会让 onprogress 彻底失效:
- PHP 后端必须关闭输出缓冲:
ob_end_clean();和ini_set('output_buffering', 'off'); - 响应头里不能有
Content-Encoding: gzip—— 分片上传时 gzip 会阻塞流式响应,导致进度事件无法及时发出 - 务必设置
Access-Control-Allow-Headers: X-Requested-With,否则某些代理在低速下会丢弃带自定义 header 的请求
进度卡死最常发生在“上传已实际进行,但前端没收到任何进度信号”的中间态。这时候靠的不是调大 timeout,而是用分片 ID + 主动查进度 + 干净响应头,三者缺一不可。











