Node.jsで記述されたサーバーと、ブラウザーで実行されているWebクライアントがあります。クライアントは、サーバーとの間でいくつかのファイルをアップロードおよびダウンロードします。サーバーは元々クライアントを配信するサーバーではないため、ここではクロスドメインの状況にあります。
サーバーは cors ミドルウェアを使用して、クロスドメインリクエストを有効にします。いくつかのカスタムヘッダーも使用しているので、次のような構成で使用します。
api.use(cors({
Origin: '*',
allowedHeaders: [ 'content-type', 'authorization', 'x-metadata', 'x-to' ],
exposedHeaders: [ 'content-type', 'content-disposition', 'x-metadata' ]
}));
対照的に、クライアントは axios を使用しており、ファイルをアップロードするコードは次のようになります。
await axios({
method: 'post',
url: 'https://localhost:3000/api/v1/add-file',
data: content,
headers: {
// ...
}
});
これまでのところ、すべてが期待どおりに機能し、特にCORSで機能します。
次に、アップロードの進行状況を追跡したいので、onUploadProgress
コールバックをaxios
コールに追加します ドキュメントで推奨 :
await axios({
method: 'post',
url: 'https://localhost:3000/api/v1/add-file',
data: content,
headers: {
// ...
},
onUploadProgress (progressEvent) {
console.log({ progressEvent });
}
});
クライアントを実行しても、アップロードは機能しますが、onUploadProgress
コールバックが呼び出されることはありません。すべてのブラウザがこれをサポートしているわけではないことを読んだことがあります(内部的には、これは基本となるonprogress
オブジェクトのXmlHttpRequest
イベントにバインドするだけです)が、最新のChromeそれを行うには(そして、CORSが関与していない別の設定を試すと、すべてが機能するように見えます)したがって、それはCORSの問題だと思います。
私は read を持っています。onprogress
イベントにリスナーを追加するとすぐに、プリフライトリクエストが送信されます。つまり、サーバーはOPTIONS
リクエストを受け入れる必要があります。そのために、サーバーに次のコードを追加しましたbeforeapi.use
への前述の呼び出し:
api.options('*', cors({
Origin: '*',
methods: [ 'GET', 'POST' ],
allowedHeaders: [ 'content-type', 'authorization', 'x-metadata', 'x-to' ],
exposedHeaders: [ 'content-type', 'content-disposition', 'x-metadata' ],
optionsSuccessStatus: 200
}));
結果:アップロードは機能しますが、onUploadProgress
イベントは発生しません。 CORSに関連する何かが欠けていると思います。問題はただ、何が欠けているのですか?
私はまた、より大きなファイル(数キロバイトではなくほぼ2ギガバイト)で同じセットアップを再実行しようとしましたが、結果は同じです。誰かが私がここで間違っていることを知っていますか?
コメントの一部に返信するには:最初に、私のaxios
バージョンは0.18.0
なので、入手可能な最新の安定版リリースを使用しています。
サーバーのソースコードはリポジトリ thenativeweb/wolkenkit-depot にあります。 CORSパートは設定されています here 。ローカルでいくつかの変更を加えたため、これは上記で投稿したバージョンとは少し異なります。ただし、ここは対処する必要がある場所です。
サーバーを構築するには、Node.jsとDockerをインストールする必要があります。次に、実行します:
$ npm install
$ npx roboter build
これにより、サーバーのコードを含む新しいDockerイメージが生成されます(このDockerイメージは、以下のクライアントテストを実行できるようにするために必要です)。サーバーで何かを変更した場合は、このDockerイメージを再構築する必要があることに注意してください。
クライアントはリポジトリ thenativeweb/wolkenkit-depot-client-js のブランチにあります issue-145-notifiy-about-progress-when-uploading-or-downloading-ファイル 。
ファイル内 DepotClient.jsonUploadProgress
にイベントリスナーを追加しました。これにより、単に例外がスローされます(これにより、イベントが発生したことが明らかになります)。
次に、このコードは this test によって実行されます(これには他にもテストがありますが、これはその1つです)。これにより例外が発生しますが、そうではありません。
テストを実行するには、Node.jsとDockerをインストールし、前述のようにサーバーを構築する必要があります。次に、次のコマンドを使用して、代わりにテストを実行します。
$ npm install
$ npx roboter test --type integration
私の期待は、少なくとも1つのaddFile
テストを提出することです。事実、すべてが緑色に変わります。つまり、アップロード自体は完全に機能しますが、onUploadProgress
イベントは発生しません。テストが実行される環境について不思議に思った場合に備えて、ここでは操り人形師を使用しています。これはヘッドレスChromeです。
そのコードは(corsの有無にかかわらず)完全に正常に動作するので、古いaxiosバージョンを使用していると想定します。 onUploadProgress
がバージョン_0.14.0
_に追加されました。
古いバージョンを使用している場合は、progress
の代わりにonUploadProgress
を使用するか、最新バージョンに更新することができます。
0.14.0(2016年8月27日)
[〜#〜] breaking [〜#〜]
progress
イベントハンドラをonUploadProgress
とonDownloadProgress
(#423)
_await axios({
method: 'post',
url: 'https://localhost:3000/api/v1/add-file',
data: content,
headers: {
// ...
},
// Old version
progress (progressEvent) {
console.log({ progressEvent });
},
// New version
onUploadProgress (progressEvent) {
console.log({ progressEvent });
}
});
_
[〜#〜]更新[〜#〜]。
提供されたコードはブラウザーで完全に機能しますが、Node.jsでaxiosを実行する場合、 _/lib/adapters/http.js
_ の代わりに _/lib/adapters/xhr.js
_
そのコードを読むと、http
アダプターにonUploadProgress
オプションがないことがわかります。
私はあなたのコードをブラウザからテストし、エンドポイントにヒットしました。 問題は、Node.jsから実行されている統合テストを実行している場合です。
ここであなたはそれに問題があります: https://github.com/axios/axios/issues/1966 ここで彼らは以下を使用することを推奨しています: progress-stream 進行したいがonUploadProress
関数でトリガーされない場合。
更新2
Puppeteerでテストを実行すると、Node.jsではなくXMLHttpRequestハンドラーを使用していることが確認できます。問題は、_try/catch
_によってキャッチされない非同期コールバック内でエラーをスローしていることです。
_try {
await request({
method: 'post',
url: `${protocol}://${Host}:${port}/api/v1/add-file`,
data: content,
headers,
onUploadProgress (progress) {
// This error occurs in a different tick of the event loop
// It's not catched by any of the try/catchs that you have in here
// Pass a callback function, or emit an event. And check that on your test
throw new Error(JSON.stringify(progress));
}
});
return id;
} catch (ex) {
if (!ex.response) {
throw ex;
}
switch (ex.response.status) {
case 401:
throw new Error('Authentication required.');
default:
throw ex;
}
}
_
これをキャッチしたい場合は、axios呼び出しをPromise
でラップし、そこで拒否する必要があります(これはテスト専用であるため、意味がありません)。
したがって、onUploadProgress
引数をaddFile
に渡すことをお勧めします。これにより、テストからonUploadProgress
に到達しているかどうかを確認できます。
これを確認するには、throw new Error(JSON.stringify(progress));
が呼び出されていたため、_{ headless: false }
_を使用してパペットを起動します。
これにより、エラーが正しくトリガーされていることがわかります。
_XMLHttpRequestUpload.onUploadProgress
_