我正在实现一个文件上传器,用户可以在其中使用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
事件之前触发error
,abort
,timeout
或loadend
事件,如{{ 3}}
查看每个浏览器的开发工具的网络选项卡,表明Chrome和Safari都识别出服务器已响应413,但Firefox未识别任何响应状态(即使在loadend
之后)。 / p>
版本为Firefox Quantum 62.0b3(64位)。 Safari是11.0.1。 Chrome是67.0.3396.99。
因此,问题是:为什么Firefox无法识别上传期间发生服务器错误,并取消Safari和Chrome可以在其中进行的上传?和我可以解决这个问题的方式?
答案 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