1

を使用して、ユーザーが 1 つ以上のファイルをアップロードできるファイル アップローダーを実装しています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 はイベントの前に、、、、またはイベントloaderror発生abortさせないようです。timeoutloadendXMLHttpRequest.upload

各ブラウザの開発ツールのネットワーク タブを見ると、Chrome と Safari の両方がサーバーが 413 で応答したことを認識していますが、Firefox は応答ステータスを認識していません (その後でもloadend)。

バージョンは Firefox Quantum 62.0b3 (64 ビット) です。サファリは11.0.1です。Chrome は 67.0.3396.99 です。

問題は、アップロード中にサーバー エラーが発生したことを Firefox が認識できず、アップロードをキャンセルできないのはなぜですか? これを解決する方法はありますか?

4

1 に答える 1

0

Cody G. が示唆したように、これは 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
于 2018-06-29T14:49:52.757 に答える