web-dev-qa-db-ja.com

PHP-AjaxでWhileループデータをフラッシュする

PHPを使用して、大きなファイルを読み取り、要求に応じて現在の行番号を送信するwhileループを作成したいと思います。 Ajaxを使用して、現在の行数を取得し、それをページに出力したいと思います。 htmlボタンを使用して、一度だけ実行され、ajaxメソッドを呼び出すjavascriptスレッドをクリックしてアクティブ化または終了できるようにしたいと思います

ショットを与えましたが、何らかの理由で、echo str_repeat(' ',1024*64);関数をコメントアウトしない限り何も出力されず、コメントアウトされると、ループ結果全体が表示されます。

1行が処理されました.2行が処理されました.3行が処理されました.4行が処理されました.5行が処理されました.6行が処理されました.7行が処理されました.8行が処理されました。9行が処理されました。10行が処理されました。

次のように別々の行に表示するのではなく、1行で表示します。

1 row(s) processed.
2 row(s) processed.
3 row(s) processed.
4 row(s) processed.
5 row(s) processed.
6 row(s) processed.
7 row(s) processed.
8 row(s) processed.
9 row(s) processed.
10 row(s) processed.

また、JavaScriptスレッドを終了する方法もわかりません。したがって、合計2つの問題があります。

 1. It's returning the entire While loop object at once instead of each time it loops.
 2. I'm not sure how to terminate the JQuery thread.

何か案は?以下はこれまでの私のコードです。

msgserv.php

<?php

//Initiate Line Count
$lineCount = 0;

// Set current filename
$file = "test.txt";

// Open the file for reading
$handle = fopen($file, "r");

//Change Execution Time to 8 Hours
ini_set('max_execution_time', 28800);

// Loop through the file until you reach the last line
while (!feof($handle)) {

    // Read a line
    $line = fgets($handle);

    // Increment the counter
    $lineCount++;

    // Javascript for updating the progress bar and information
    echo $lineCount . " row(s) processed.";

    // This is for the buffer achieve the minimum size in order to flush data
    //echo str_repeat(' ',1024*64);

    // Send output to browser immediately
    flush();

    // Sleep one second so we can see the delay
    //usleep(100);
}

// Release the file for access
fclose($handle);

?>

asd.html

<html>
    <head>
        <script src="http://code.jquery.com/jquery-latest.min.js" type="text/javascript" charset="utf-8"></script>

        <style type="text/css" media="screen">
            .msg{ background:#aaa;padding:.2em; border-bottom:1px #000 solid}
            .new{ background-color:#3B9957;}
            .error{ background-color:#992E36;}
        </style>

    </head>
    <body>

    <center>
        <fieldset>
            <legend>Count lines in a file</legend>
            <input type="button" value="Start Counting" id="startCounting" />
            <input type="button" value="Stop Counting!" onclick="clearInterval(not-Sure-How-To-Reference-Jquery-Thread);" />
        </fieldset>
    </center>

    <div id="messages">
        <div class="msg old"></div>
    </div>

    <script type="text/javascript" charset="utf-8">
        function addmsg(type, msg){
            /* Simple helper to add a div.
        type is the name of a CSS class (old/new/error).
        msg is the contents of the div */
            $("#messages").append(
            "<div class='msg "+ type +"'>"+ msg +"</div>"
        );
        }

        function waitForMsg(){
            /* This requests the url "msgsrv.php"
        When it complete (or errors)*/
            $.ajax({
                type: "GET",
                url: "msgsrv.php",
                async: true, /* If set to non-async, browser shows page as "Loading.."*/
                cache: false,
                timeout:2880000, /* Timeout in ms set to 8 hours */

                success: function(data){ /* called when request to barge.php completes */
                    addmsg("new", data); /* Add response to a .msg div (with the "new" class)*/
                    setTimeout(
                    'waitForMsg()', /* Request next message */
                    1000 /* ..after 1 seconds */
                );
                },
                error: function(XMLHttpRequest, textStatus, errorThrown){
                    addmsg("error", textStatus + " (" + errorThrown + ")");
                    setTimeout(
                    'waitForMsg()', /* Try again after.. */
                    "15000"); /* milliseconds (15seconds) */
                },
            });
        };

        $('#startCounting').click(function() {
            waitForMsg();
        });
    </script>

</body>
</html>

test.txt

1
2
3
4
5
6
7
8
9
10
20
ThreaT

使用:

1つのphpスレッドで必要なすべてを実行する必要があります

[〜#〜]編集[〜#〜]

Nickbの答えを見てください。これを簡単に行う方法を探している場合は、次のアルゴリズムになります。

  1. javascriptが開きますprocess.php ajax(すべての作業とステータスレポートの印刷を行います)を介して、jQueryajaxが継続的な読み込みをサポートしているかどうかを調べる必要があります
  2. ユーザーが更新を停止することを決定した場合、提供されたリンクに表示されているように読み込みを強制終了します

process.php

ignore_user_abort(); // Script will finish in background
while(...){
  echo "Page: $i\n";
  ob_flush();
}

EDIT 2リクエストされた例(少し異なり、醜いですが、単純です)test_process.php

// This script will write numbers from 1 to 100 into file (whatever happens)
// And sends continuously info to user
$fp = fopen( '/tmp/output.txt', 'w') or die('Failed to open');
set_time_limit( 120);
ignore_user_abort(true);

for( $i = 0; $i < 100; $i++){
    echo "<script type=\"text/javascript\">parent.document.getElementById( 'foo').innerHTML += 'Line $i<br />';</script>";
    echo str_repeat( ' ', 2048);
    flush();
    ob_flush();
    sleep(1);
    fwrite( $fp, "$i\n");
}

fclose( $fp);

そしてメインのhtmlページ:

<iframe id="loadarea"></iframe><br />
<script>
function helper() {
    document.getElementById('loadarea').src = 'test_process.php';
}
function kill() {
    document.getElementById('loadarea').src = '';
}
</script>

<input type="button" onclick="helper()" value="Start">
<input type="button" onclick="kill()" value="Stop">
<div id="foo"></div>

次のようにスタートラインを打った後:

Line 1
Line 2

Divに表示されます#fooStopを押すと、それらは表示されなくなりましたが、スクリプトはバックグラウンドで終了し、100個の数字すべてをファイルに書き込みました。

もう一度Startを押すと、スクリプトは最初から実行を開始し(ファイルの書き換え)、並列要求も実行します。

Httpストリーミングの詳細については、 このリンク を参照してください。

9
Vyktor

PHPとAJAXの相互作用について混乱しています。

AJAXを介してPHPページをリクエストすると、PHPスクリプトの実行が開始されます。ただし、flush()を使用してクリアすることもできます。内部PHPバッファー、AJAX呼び出しは、接続が閉じられるまで終了しません(つまり、応答ハンドラーは呼び出されません)。ファイル全体が読み取られたとき。

あなたが探していることを達成するために、私はあなたがこのような並列プロセスフローを必要とするだろうと信じています:

  1. 最初のAJAX postは、ファイルの読み取りを開始する要求を送信します。このスクリプトは、unqiue IDを生成し、それをブラウザーに送り返し、実際にファイルの読み取りを行うスレッドを生成して、終了します。
  2. 後続のすべてのAJAXリクエストは、ファイル読み取りのステータスをチェックする別のPHPスクリプトに送られます。この新しいPHPスクリプト#1で生成された一意のIDに基づいて、ファイル読み取りの現在のステータスを送信し、終了します。

このプロセス間通信は、$_SESSION変数を使用するか、データをデータベースに保存することで実現できます。いずれにせよ、現在の順次実装ではなく並列実装が必要です。そうしないと、ステータス全体を一度に取得し続けることになります。

11
nickb

より簡単な解決策は、ネイティブ(vanila js) [〜#〜] xhr [〜#〜] オブジェクトを使用することです。

ロングポーリング について、非常に洗練されたソリューションがあります。

PHP:

<?php
header('Content-Type: text/html; charset=UTF-8');
if (ob_get_level() == 0) ob_start();
for ($i = 0; $i<10; $i++){
  echo "<br> Line to show.";
  echo str_pad('',4096)."\n";
  ob_flush();
  flush();
  sleep(2);
}
echo "Done.";
ob_end_flush();

JS:

var xhr = new XMLHttpRequest();
xhr.open('GET', '/api/some_service.php', true);

xhr.send(null);
xhr.onreadystatechange = function() {
  if (xhr.status == 200) {
    if (xhr.readyState == XMLHttpRequest.LOADING){
      console.log('response',xhr.response);
      // this can be also binary or your own content type 
      // (Blob and other stuff)
    }
    if (xhr.readyState == XMLHttpRequest.DONE){
      console.log('response',xhr.response);
    }
  }
}
3
JonnieJS

秘訣は、ファイル(Ajaxを介して更新)を作成し、setIntervalを使用してその値を取得してから、プログレスバーを更新することです。

0
Didier Sampaolo