html表单原生不支持上传进度显示,必须用xmlhttprequest.upload.onprogress监听上传事件;fetch无法监听上传进度;需注意服务端配置、formdata构造、兼容性及ui更新节流等问题。

HTML 表单本身不支持上传进度显示
原生 <form></form> 提交是整页跳转或刷新行为,没有内置的进度事件。所谓“表单显示上传进度”,实际必须绕开传统提交,改用 XMLHttpRequest 或 fetch + FormData 手动发请求,并监听上传阶段的事件。
常见错误现象:onprogress 一直不触发、event.loaded 始终为 0、进度条卡在 0% 或直接跳到 100%。
- 必须用
XMLHttpRequest.upload.onprogress,不是XMLHttpRequest.onprogress(后者是下载事件) -
fetch不提供上传进度 API,无法直接监听;强行用ReadableStream分块上传会破坏服务端对multipart/form-data的解析 - 服务端不能禁用或截断
Content-Length头,否则浏览器无法计算总大小
用 XMLHttpRequest.upload.onprogress 获取实时进度
这是目前最稳定、兼容性最好(IE10+)、且能精确反映上传状态的方式。关键在于绑定事件的位置和时机。
使用场景:需要显示百分比、上传速度、剩余时间,或限制大文件上传时做中途取消。
立即学习“前端免费学习笔记(深入)”;
1、对ASP内核代码进行DLL封装,从而大大提高了用户的访问速度和安全性;2、采用后台生成HTML网页的格式,使程序访问速度得到进一步的提升;3、用户可发展下级会员并在下级购买商品时获得差额利润;4、全新模板选择功能;5、后台增加磁盘绑定功能;6、后台增加库存查询功能;7、后台增加财务统计功能;8、后台面值类型批量设定;9、后台财务曲线报表显示;10、完善订单功能;11、对所有传输的字符串进行安全
const xhr = new XMLHttpRequest();
xhr.open('POST', '/upload');
xhr.upload.onprogress = function (e) {
if (e.lengthComputable) {
const percent = (e.loaded / e.total) * 100;
console.log(`上传中:${Math.round(percent)}%`);
}
};
xhr.send(formData);-
e.lengthComputable一定要判断,某些代理或服务端配置下该值为false,此时e.total是 0 - 不要在
xhr.onload里读取responseText来判断成功——上传完成 ≠ 服务端处理完成,得等xhr.status === 200 - 如果后端用了 Nginx,默认
client_max_body_size限制可能让大文件在到达应用前就被拦截,此时根本不会触发onprogress
FormData 构建要避开常见陷阱
进度能跑起来,不代表数据一定能被后端正确接收。FormData 看似简单,但字段顺序、空值、Blob 类型都会影响 multipart 边界和解析结果。
参数差异:append() 和 set() 行为不同;重复 key 会被合并还是覆盖,取决于后端框架(如 Express 的 multer 默认只取第一个)。
- 文件字段名必须和服务端约定一致,比如后端 expect
file,你传了myFile就收不到 - 不要手动设置
Content-Type请求头——XMLHttpRequest会自动设为multipart/form-data; boundary=xxx,设了反而会导致边界错乱 - 如果同时传 JSON 字符串和文件,别把 JSON 序列化后
append('data', JSON.stringify(obj)),应改用纯字段拆解,或走双请求(先传元数据,再传文件)
移动端和 Safari 的兼容性坑
iOS Safari 对 XMLHttpRequest.upload 的支持基本没问题(iOS 10+),但实际表现常受制于系统级限制:比如后台上传被暂停、Wi-Fi 切换蜂窝网络时中断、甚至某些版本对 File 对象的 size 读取不准。
性能影响:频繁更新 UI(如每 50ms 改一次进度条)在低端 Android 机上可能造成卡顿,建议节流。
- 不要依赖
File.size作为e.total的校验值——Safari 在某些 FilePicker 场景下返回0,但上传时e.total是正确的 - 微信内嵌 WebView(X5 内核)对
onprogress触发频率有压制,实测可能 3–5 秒才更新一次,需在 UI 上提示“上传中,请稍候”而非强求实时性 - 如果用户点了「取消」,调用
xhr.abort()后,部分安卓机型仍会继续上报少量onprogress事件,需在回调里加if (xhr.readyState === 0) return防止误更新
事情说清了就结束。进度条背后不是表单的事,是请求生命周期、浏览器实现细节和后端配合共同决定的。漏掉任意一环,UI 上看到的就只是假象。










