为什么XMLHttpRequest上传在Firefox中无法正常失败?

时间:2018-06-29 11:35:16

标签: javascript firefox xmlhttprequest xmlhttprequest-level2 firefox-quantum

我正在实现一个文件上传器,用户可以在其中使用XMLHttpRequest上传一个或多个文件。我使用fetch,因为我需要能够向用户提供有关上传进度的视觉反馈。

我遇到的问题是服务器在完成上传之前停止处理上传(例如,如果上传的文件太大,则使用413 Payload Too Large错误关闭连接)。如果在使用Safari或Chrome浏览器时发生此类错误,他们将按我的意愿中止上传。

但是,在Firefox中,它似乎忽略了这一点,并在停止之前重试了几次上传。

我的代码如下:

// Initialize a new request object.
let req = new XMLHttpRequest();

// Set expected response as JSON.
req.responseType = 'json';

// Set event handlers.
req.upload.onreadystatechange = function(e) { console.log(e.type); }
req.upload.onuploadstart = function(e) { console.log(e.type); }
req.upload.onprogress = function(e) { console.log(e.type); }
req.upload.onabort = function(e) { console.log(e.type); }
req.upload.onload = function(e) { console.log(e.type); }
req.upload.ontimeout = function(e) { console.log(e.type); }
req.upload.onuploadend = function(e) { console.log(e.type); }

// Open request, set request header.
req.open('POST', '/some-endpoint', true);
req.setRequestHeader('Content-type', 'multipart/form-data;boundary=---some-boundary---');

// Create FormData object to submit.
let fd = new FormData(formElement);

// Send data.
req.send(fd);

在Safari和Chrome中,当我上传的文件太大而导致服务器无法接受时,导致服务器以413状态响应关闭连接,事件将按以下顺序触发:

loadstart
progress (multiple)
Failed to load resource (413 Request Entity Too Large)

如我所料。在Firefox中,事件按以下顺序触发:

loadstart
progress (multiple, ignoring connection closes and restarting upload multiple times)
loadend

Firefox似乎没有在load事件之前触发erroraborttimeoutloadend事件,如{{ 3}}

查看每个浏览器的开发工具的网络选项卡,表明Chrome和Safari都识别出服务器已响应413,但Firefox未识别任何响应状态(即使在loadend之后)。 / p>

版本为Firefox Quantum 62.0b3(64位)。 Safari是11.0.1。 Chrome是67.0.3396.99。

因此,问题是:为什么Firefox无法识别上传期间发生服务器错误,并取消Safari和Chrome可以在其中进行的上传?我可以解决这个问题的方式?

1 个答案:

答案 0 :(得分:0)

Cody G. suggested一样,这可能是Firefox中的错误或与错误有关。

这不能回答原始问题。但是,它确实提供了一种解决方法,并希望为其他任何人提供一些潜在的启发性信息。

Firefox,Safari和Chrome浏览器在上传成功后(即服务器在上传完成前未发送回响应或关闭连接时)以相同的顺序触发所有事件。该命令是:

readystatechange (readyState = 1)
loadstart
progress (1...n times)
load
loadend
readystatechange (readyState = 2)
readystatechange (readyState = 4)

...按预期。

当上传失败(即服务器关闭连接并发回响应)时,Safari和Chrome会以相同的顺序触发事件。该命令是:

readystatechange (readyState = 1)
loadstart
progress (1...n times)
[the server responds with an error, which does *not* trigger an error event]
readystatechange (readyState = 2)
readystatechange (readyState = 3)
readystatechange (readyState = 4)
另一方面,

Firefox会在上传失败时按以下顺序触发事件:

readystatechange (readyState = 1)
loadstart
progress (1...n times, including retrying from the start more than once when the server responds or closes the connection)
readystatechange (readyState = 2)
readystatechange (readyState = 3)
readystatechange (readyState = 4)
error
loadend

我防止Firefox无缘无故地多次重启上传的变通办法是包括一个变量,该变量可以跟踪以前的加载量:

let prevLoaded = 0;
xhr.upload.addEventListener('progress', function(e) {
    if (prevLoaded !== 0 && e.loaded <= prevLoaded) {
        xhr.abort();
        return;
    }
    prevLoaded = e.loaded;
}, false);

这将导致请求被取消。 Safari和Chrome不会运行此代码,因为其他事件可能会触发。使用此代码后,对于不成功的上传,Firefox的事件触发顺序将变为:

readystatechange (readyState = 1)
loadstart
progress (1...n times, but almost always stopping after the server closes the connection or responds with an error)
readystatechange (readyState = 4)
abort
loadend