XMLHttpRequest.upload progress事件在Safari/Firefox不触发(Chrome正常)
问题成因
- 测试插入
console.log('Hello')的写法存在语法错误:addEventListener第二个参数要求传入函数引用,直接将console.log('Hello')写在参数位时,这段代码会在事件绑定瞬间立即执行,不会等待progress事件触发;同时参数间缺少逗号分隔,导致非Chrome浏览器解析代码时,不会将fillProgressBar注册为progress事件回调,这是控制台能输出Hello但回调始终不触发的直接原因。 - 浏览器实现差异:Safari、Firefox对XHR上传进度事件有额外触发限制——必须在调用
xhr.send()之前,给XHR对象本身绑定至少一个事件监听器(load、error事件均可),否则浏览器会静默屏蔽xhr.upload上的所有progress事件,不会派发回调。Chrome没有这个限制,因此原始代码在Chrome下可以正常运行。 - 缺少默认行为阻止:代码没有在表单submit事件回调里阻止原生表单提交逻辑,XHR发起后浏览器很快会触发原生表单跳转,直接中断正在运行的XHR请求,Safari和Firefox不会为被中断的请求派发剩余进度事件,进一步导致进度条无响应。
可行修复方案
- 修正事件绑定逻辑,不要在监听器参数位直接写执行语句,需要打日志时要把逻辑包裹在回调函数内部。
- 在表单submit回调第一行阻止默认提交行为,避免原生跳转中断XHR请求。
- 给XHR对象本身补充基础的load、error事件监听,触发Safari、Firefox的进度事件派发逻辑。
- 补充必要的请求头,确保后台返回带Content-Length的响应,保证进度值可以正常计算。
修复后的完整可运行代码如下:
function uploadFile(event){ // 阻止原生表单提交跳转,避免中断XHR请求 event.preventDefault() const xhr = new XMLHttpRequest() xhr.open( 'POST', event.currentTarget.getAttribute('action') ) // 标识AJAX请求,兼容Prestashop后台的请求校验 xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest') // 关键:给xhr对象本身绑定基础事件,触发Safari/Firefox的upload进度事件派发 xhr.addEventListener('load', () => { // 可自行补充上传完成后的业务逻辑,比如提示成功、刷新页面等 progressInformation.textContent = '上传完成' }) xhr.addEventListener('error', () => { progressInformation.textContent = '上传失败,请重试' }) // 正确绑定progress事件回调 xhr.upload.addEventListener( 'progress', fillProgressBar ) xhr.send(new FormData(event.currentTarget)) } const progressElement = document.body.querySelector('progress') const progressInformation = document.body.querySelector('#progress_information') function fillProgressBar({lengthComputable, loaded, total}){ if (!lengthComputable) { progressInformation.textContent = '计算上传进度中...' return } const progression = ((loaded / total) * 100).toFixed(0) progressInformation.textContent = `${progression}%` progressElement.value = progression } const customForm = document.body.querySelector('#custom_form'); customForm.addEventListener( 'submit', uploadFile )
额外注意事项
- 如果在Prestashop中开启了CSRF防护,请确认提交的FormData中已经包含后台要求的校验token,否则请求会被后台拦截,也会导致进度事件异常。
- 部分低版本Safari在HTTP/2环境下对大文件分块上传的长度计算存在兼容问题,如果遇到
lengthComputable始终为false的情况,可以配置服务器端关闭对应接口的分块传输编码,返回明确的Content-Length头即可。
内容的提问来源于stack exchange,提问作者Florian
相关产品推荐
相关产品推荐

