web-dev-qa-db-ja.com

データをブラウザにフラッシュするが、実行は継続する方法

ob_start()と対応するob_flush()があります。データの一部をフラッシュし、残りを実行し続けたいと思います。 ob_flush()を使用しても効果がありませんでした。また、可能であれば、ブラウザにロードを表示せずに残りを実行する必要があります。

編集:

Ajaxを使いたくない

20
aWebDeveloper

ob_flushはバッファを書き込みます。言い換えれば、ob_flushはPHPにApache(またはnginx/lighttpd/whatever)に出力を与えるように指示し、次にPHPにそれを忘れさせるように指示します。Apacheが出力を取得すると、Apacheは必要な処理をすべて実行します(つまり、ob_flushの後で、ブラウザにすぐに書き込まれるかどうかは制御不能です)。

だから、短い答え:それを行うための保証された方法はありません。

おそらく、おそらくAJAXを探しているでしょう。あなたがしているようにページコンテンツがロードされるときに人々が操作しようとしているときはいつでも、AJAXはほとんど常に正しいパスです。

バックグラウンドでタスクを続行する場合は、ignore_user_abortを使用できます。詳しくは here を使用しますが、これは最適な方法ではない場合があります。あなたは本質的にそのスレッドを制御できなくなり、私の意見では、Webサーバーのスレッドは重い処理が属する場所ではありません。

私はウェブに面したものからそれを抽出しようとします。これは、cronエントリ、またはPHPの内部からバックグラウンドプロセスを生成することを意味する可能性があります(スクリプトの実行の内部から開始されたプロセスはスクリプトで終了せず、スクリプトは待機しません。死ぬ前にそれを終了する)。

そのルートをたどると、必要に応じて何らかのステータスシステムを作成できることになります。次に、実行を監視し、進行状況を定期的にユーザーに更新できます。 (技術的には、ignore_user_abort- edスクリプトを使用してステータスシステムを作成することもできますが、私にはそれほどきれいには見えません。)

15
Corbin

私は過去にこれを行ったことがあり、これが私がそれを解決した方法です:

_ob_start();

/*
 * Generate your output here
 */ 

// Ignore connection-closing by the client/user
ignore_user_abort(true);

// Set your timelimit to a length long enough for your script to run, 
// but not so long it will bog down your server in case multiple versions run 
// or this script get's in an endless loop.
if ( 
     !ini_get('safe_mode') 
     && strpos(ini_get('disable_functions'), 'set_time_limit') === FALSE 
){
    set_time_limit(60);
}

// Get your output and send it to the client
$content = ob_get_contents();         // Get the content of the output buffer
ob_end_clean();                      // Close current output buffer
$len = strlen($content);             // Get the length
header('Connection: close');         // Tell the client to close connection
header("Content-Length: $len");     // Close connection after $len characters
echo $content;                       // Output content
flush();                             // Force php-output-cache to flush to browser.
                                     // See caveats below.

// Optional: kill all other output buffering
while (ob_get_level() > 0) {
    ob_end_clean();
}
_

以前にいくつかのコメントで述べたように、コンテンツの長さは変更されますが、ヘッダーは変更されないため、コンテンツのgzip圧縮には注意してください。また、出力をバッファリングできるため、クライアントにすぐに送信されません。
Apache_setenv('no-gzip', '1');を使用して、コンテンツをgzipしないようにApacheに通知してみることができます。ただし、rewrite-rulesを使用してページに移動すると、これらの環境変数も変更されるため、これは機能しません。少なくとも、私にとってはそうでした。

manual で、コンテンツをユーザーにフラッシュすることに関するその他の警告を参照してください。

16
Zombaya

これは私の機能です

_function bg_process($fn, $arr) {
    $call = function($fn, $arr){
        header('Connection: close');
        header('Content-length: '.ob_get_length());
        ob_flush();
        flush();
        call_user_func_array($fn, $arr);
        };
    register_shutdown_function($call, $fn, $arr);
    }
_

phpが接続を閉じた後、最後に実行される関数をラップします。そしてもちろん、ブラウザはバッファリングを停止します。

_function test() {
    while (true) {
        echo 'this text will never seen by user';
        }
    }
_

これは関数を呼び出す方法です

_bg_process('test'); 
_

最初の引数は callable で、2番目の引数はインデックス付き配列で 'test'関数に渡される配列です

注:スクリプトの冒頭ではob_start()を使用していません。

4
Afrig Aminuddin

これが私のブログのApache/mod_phpを使用してどのように実現できるかを説明している記事があります http://codehackit.blogspot.com/2011/07/how-to-kill-http-connection-and.html これがお役に立てば幸い

2
smassey

fastcgi_finish_request

この関数は、すべての応答データをクライアントにフラッシュし、要求を完了します。これにより、クライアントへの接続を開いたままにせずに、時間のかかるタスクを実行できます。

apacheでは機能しません。(PHP 5> = 5.3.3、PHP 7)

0
sun

PHP-FPMを使用している場合:

ignore_user_abort(true);
fastcgi_finish_request();

上記の2つの関数は、ignore_user_abortはエラーを防ぎ、fastcgi_finish_requestクライアント接続を閉じます。

0
Nick Tsai