web-dev-qa-db-ja.com

Nginx PHP大きなファイルのアップロードで失敗(6 GB以上)

6GBを超える大きなファイルをアップロードすると、非常に奇妙な問題が発生します。私のプロセスは次のように機能します。

  1. ファイルはAjax経由でphpスクリプトにアップロードされます。
  2. PHPアップロードスクリプトは$ -FILEを取得し、 この回答 のようにtmpの場所にチャンクでコピーします。
  3. ファイルの場所はdbに保存されます
  4. Cronスクリプトは、後でファイルをs3にアップロードし、再びfopen関数とバッファリングを使用してメモリ使用量を低く抑えます

PHP(HHVM)とNGINXの両方の構成には、最大16GBのファイルを許可するように構成されていますが、テストファイルは8GBのみです。

奇妙な部分があります。ajaxは[〜#〜] always [〜#〜]タイムアウトします。ただし、ファイルは正常にアップロードされ、tmpの場所、db、s3などに保存されている場所にコピーされます。ただし、AJAXは1時間でも実行されますすべての実行が終了した後(10-15分かかります)、タイムアウトになったときにのみ終了します。

サーバーが大きなファイルに対してのみ応答を送信しない原因は何ですか?

また、サーバー側のエラーログは空です。

13
Devin Dixon

大きなファイルのアップロードは、費用がかかり、エラーが発生しやすい操作です。 Nginxとバックエンドは、遅いディスクIOが発生した場合、理論的にはmultipart/form-data encoding RFC 1867を使用してファイルのアップロードを管理するのが簡単です。

Multipart/form-data本文の developer.mozilla.org によると、HTTP Content-Disposition一般ヘッダーは、フィールドに関する情報を提供するためにmultipart本文のサブパートで使用できるヘッダーですに適用されます。サブパートは、Content-Typeヘッダーで定義された境界で区切られます。ボディ自体で使用されるContent-Dispositionは効果がありません。

ファイルのアップロード中に何が起こるか見てみましょう:

1)クライアントは、ファイルコンテンツを含むHTTP要求をWebサーバーに送信します

2)Webサーバーは要求を受け入れ、データ転送を開始します(または、ファイルサイズが制限を超えている場合はエラー413を返します)

3)Webサーバーがバッファの読み込みを開始します(ファイルとバッファのサイズに依存)

4)Webサーバーは、ファイル/ネットワークソケットを介してファイルコンテンツをバックエンドに送信します

5)バックエンドは最初のリクエストを認証します

6)バックエンドはファイルを読み取り、ヘッダーをカットします(Content-Disposition、Content-Type)

7)ディスク上のバックエンドダンプ結果ファイル

8)データベースの変更などのフォローアップ手順

client_body_in_file_only off;

大きなファイルのアップロード中に、いくつかの問題が発生します。

  • hTTP本文リクエストはディスクにダンプされ、ファイルを処理してコピーするバックエンドに渡されます
  • hTTPリクエストコンテンツがサーバーにアップロードされる前にリクエストを認証することはできません
  • 一方、大きなファイルバックエンドをアップロードする場合、すぐにファイルコンテンツ自体を必要とすることはほとんどありません

新しい場所で構成されたNginxから始めましょうhttp:// backend/upload大きなファイルのアップロードを受信するには、バックエンドの対話は最小限に抑えられます(Content-Legth:0)、fileディスクにのみ保存されています。バッファーを使用してNginxはファイルをディスクにダンプします(ファイルはランダムな名前で一時ディレクトリに保存され、変更できません)。その後にPOSTバックエンドからロケーションへのリクエストhttp:// backend/fileX-File-Nameヘッダーにファイル名を含む。

追加の情報を保持するには、最初のPOST要求でヘッダーを使用します。たとえば、最初の要求のX-Original-File-Nameヘッダーを使用すると、ファイルと必要なマッピング情報をデータベースに保存します。

client_body_in_file_only on;

それを実現する方法を見てみましょう。

1)Nginxを設定して、HTTP本文コンテンツをファイルにダンプし、保存し続けるclient_body_in_file_only on;

2)新しいバックエンドエンドポイントを作成します http:// backend/file 一時ファイル名とヘッダー間のマッピングを処理しますX-File-Name

4)instrument AJAX header with query X-File-Name Nginxはpost upload requestの送信に使用します

構成:

location /upload {
  client_body_temp_path      /tmp/;
  client_body_in_file_only   on;
  client_body_buffer_size    1M;
  client_max_body_size       7G;

  proxy_pass_request_headers on;
  proxy_set_header           X-File-Name $request_body_file; 
  proxy_set_body             off;
  proxy_redirect             off;
  proxy_pass                 http://backend/file;
}

Nginx設定オプション client_body_in_file_only はマルチパートデータアップロードと互換性がありませんが、AJAXつまりXMLHttpRequest2(バイナリデータ))で使用できます。

バックエンド認証が必要な場合、処理する唯一の方法は auth_request を使用することです。たとえば:

location = /upload {
  auth_request               /upload/authenticate;
  ...
}

location = /upload/authenticate {
  internal;
  proxy_set_body             off;
  proxy_pass                 http://backend;
}

client_body_in_file_only on; auth_request on;

アップロード前の認証ロジックは、初期のPOST Content-Lengthサイズに関係なく、認証されていない要求から保護します。

46
Anatoly