2012-01-17 25 views
7

私のVPSサーバーに次の問題があります。同時PHPスクリプトの実行

私はブラウザに大きなファイルを送信する長時間実行するPHPスクリプトを持っています。

<?php 
header("Content-type: application/octet-stream"); 
readfile("really-big-file.zip"); 
exit(); 
?> 

これは、基本的にサーバーのファイルシステムからファイルを読み取り、ブラウザに送信します。アプリケーションにビジネスロジックが適用される必要があるため、私は直接リンクを使用するだけでは(そしてApacheにファイルを提供させることもできません)。

このようなダウンロードの実行中は、サイトが他のリクエストに応答しないという問題があります。

+0

これは問題ではありませんが、サイズの大きいファイルを扱うときは常に 'set_time_limit(0);'を呼び出すべきです。現時点ではあなたのために何も変わるべきではありませんが、ある時点でこれを* * Windowsプラットフォームに移すと、潜在的な問題が発生する可能性があります。 – DaveRandom

+1

どのように問題を発見しましたか?あなたは同じマシンから複数のリクエストを行うことでこれをテストしていますか?あなたはセッションを使っていますか? – DaveRandom

+0

@DaveRandom私は複数のファイルをダウンロードしようとしたときに問題に気付きました(それらはダウンロードのためにキューに入れられました)。私はセッションを使用しています - 試してみましたが、この制限は他のセッションに影響しないようです。あなたの考えをありがとう - 私はさらに調査するつもりです。 –

答えて

30

あなたがセッションを使用しているという事実に関連して発生している問題。スクリプトに実行中のセッションがある場合、セッションファイルをロックして、セッションデータを破損する可能性のある同時書き込みを防止します。つまり、同じセッションIDを使用する同じクライアントからの複数のリクエストは同時に実行されず、キューに入れられ、一度に1つしか実行できません。

複数のユーザーは、異なるセッションIDを使用するため、この問題は発生しません。これは、ファイルがダウンロードされている間にサイトにアクセスしたり、一度に複数のファイルをダウンロードしたりすることが考えられるため、問題がないことを意味するわけではありません。

解決方法は実際には非常に簡単です:ファイルを出力する前にsession_write_close()を呼び出してください。これにより、セッションファイルが閉じられ、ロックが解除され、さらに同時要求が実行されます。

+0

ありがとう、それは問題でした。 –

+0

良い点と+1 – Lion

+0

シンプルに優れています。あなたのおかげで幸せになれた! – Claudix

1

サーバーはどのように他の要求に応答しませんか?それは "待っているexample.com ..."ですか、それとも何らかのエラーが出ますか?

私は似たようなことをしますが、クライアントがチャンクを受け入れてダウンロードしている間に、ファイルシステムに壊れたファイルを提供します。これは一度にすべてを提供するよりも優れています。ファイルシステムとサーバー全体を管理します。

編集:この質問の答えではありませんが、質問者はチャンクされたファイルを読むことについて尋ねました。ここで私が使用する関数があります。ファイルへのフルパスを指定します。

function readfile_chunked($file_path, $retbytes = true) 
{ 
$buffer = ''; 
$cnt = 0; 
$chunksize = 1 * (1024 * 1024); // 1 = 1MB chunk size 
$handle = fopen($file_path, 'rb'); 
if ($handle === false) { 
    return false; 
} 
while (!feof($handle)) { 
    $buffer = fread($handle, $chunksize); 
    echo $buffer; 
    ob_flush(); 
    flush(); 
    if ($retbytes) { 
     $cnt += strlen($buffer); 
    } 
} 
    $status = fclose($handle); 
    if ($retbytes && $status) { 
     return $cnt; // return num. bytes delivered like readfile() does. 
} 
    return $status; 
} 
+0

新しい要求がダウンロードの完了を待っています。私はどのようにチャンクのダウンロードをPHPで実装できるのかよく分かりません。 –

+0

こんにちは、エミールM.私は、チャンクでファイルを読み取るために使用する関数を追加しました。 –

+0

あなたの答えはちょうど-1だったので、私に説明させてください。 readfileはファイルの内容をメモリに読み込まず、stdoutに書き出します。いいえ、内部関数 '_php_stream_passthru()'を呼び出します。これはあなたのCループと似ていますが、 'output_buffering' INI設定がこれを実現するので、フラッシュを行いません。すでに圧縮されたコンテンツを出力するには、 'zlib.output_compression'などはfalseでなければなりません。あなたの提案は、利益を上げずに複雑さを増すだけです。 – TerryE

0

私は(小さなチャンク内のファイルを読み取り、送信、[PHPドキュメント内readfileのコメントを見る]梨にHTTP_Downloadを使用して)異なるアプローチを試してみましたが、ファイルが大きくなっているとき、私は常にパフォーマンスの問題に遭遇しました。

ビジネスロジックを実行してダウンロードをApacheに委譲できるApache mod があります。ダウンロードは一般公開されません。私は、これが問題の最も洗練された解決策だと思います。

詳細:

2

あなたのサーバーの設定は、確認する必要がある唯一の場所ではありません。

通常どおりブラウザからリクエストを行い、他のクライアントから別のリクエストを行ってください。

同じマシンのwgetまたは別のマシンの別のブラウザーのいずれか。

0

同じことが起こり、私はセッションを使用していません。 session.auto_startが0に設定されています 私のスクリプト例は "sleep(5)"しか実行せず、最初に "session_write_close()"を追加しても問題は解決しません。

0

httpd.confファイルを確認してください。たぶんあなたはKeepAlive Onを持っているかもしれません。そのため、最初のリクエストが完了するまで、2回目のリクエストがハングアップするのです。一般的にPHPスクリプトでは、訪問者が長時間待たされるべきではありません。大きなものをダウンロードする必要がある場合は、別の内部要求でそれを行い、そのユーザーは直接制御できません。完了するまで、エンドユーザに「実行中」ステータスを返し、完了したら実際の結果を処理します。

関連する問題