You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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不会为被中断的请求派发剩余进度事件,进一步导致进度条无响应。
可行修复方案
  1. 修正事件绑定逻辑,不要在监听器参数位直接写执行语句,需要打日志时要把逻辑包裹在回调函数内部。
  2. 在表单submit回调第一行阻止默认提交行为,避免原生跳转中断XHR请求。
  3. 给XHR对象本身补充基础的load、error事件监听,触发Safari、Firefox的进度事件派发逻辑。
  4. 补充必要的请求头,确保后台返回带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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 08:44:04