Почему при загрузке XMLHttpRequest в Firefox происходит сбой?

Я реализую загрузчик файлов, где пользователь может загрузить один или несколько файлов, используя 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 событие, как указано в XMLHttpRequest.upload документация

Просмотр сетевых вкладок каждого из инструментов разработчика браузеров показывает, что Chrome и Safari оба распознают, что сервер ответил 413, но Firefox не распознал никакого статуса ответа (даже после того, как loadend).

Версии Firefox Quantum 62.0b3 (64-разрядная версия). Safari - это 11.0.1. Chrome 67.0.3396.99.

Итак, вопрос: почему Firefox не может распознать, что произошла ошибка сервера во время загрузки, и отменить загрузку, где Safari и Chrome могут? и есть ли способ, которым я могу решить это?

1 ответ

Решение

Как предположил Коди Г., это может быть ошибка или связанная с ней ошибка в 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
Другие вопросы по тегам