79

私はこの質問を読みましたが、それは私の質問に正確に答えていません。残念ながら、最後にAJAXを確認してから、XHRオブジェクトの内容が変更されたようです。そのため、データのresponseText入力が完了する前に直接アクセスすることはできなくなりました。

AJAX(できればjQueryですが、提案を受け付けています)を使用して、制御できないサーバーからHTTP経由でCSVデータを取得するページを作成する必要があります。応答データは非常に大きくなる可能性があります。メガバイトのテキストは珍しいことではありません。

サーバーはストリームフレンドリーです。JavaScriptから直接、返されるデータのストリームにアクセスする方法はまだありますか?

真ん中に存在し、ある種の「Comet」技術(ロングポーリング、EventSourceなど)を使用するPHPコードを作成するオプションはありますが、可能であればそれを避けたいと思います。

関連する場合は、この質問に対して、ユーザーがFirefox / Chrome / Operaの最新バージョンを使用しており、古いブラウザーの互換性は問題ではないと想定してください。

4

6 に答える 6

85

これは、テキストまたはHTMLを出力するときに非常に簡単です。以下に例を示します。

(ただし、 JSONを出力しようとすると問題が発生しますが、これについてはさらに詳しく説明します。)

PHPファイル

header('Content-type: text/html; charset=utf-8');
function output($val)
{
    echo $val;
    flush();
    ob_flush();
    usleep(500000);
}
output('Begin... (counting to 10)');
for( $i = 0 ; $i < 10 ; $i++ )
{
    output($i+1);
}
output('End...');

HTMLファイル

<!DOCTYPE>
<html>
    <head>
        <title>Flushed ajax test</title>
        <meta charset="UTF-8" />
        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
    </head>
    <body>
        <script type="text/javascript">
        var last_response_len = false;
        $.ajax('./flushed-ajax.php', {
            xhrFields: {
                onprogress: function(e)
                {
                    var this_response, response = e.currentTarget.response;
                    if(last_response_len === false)
                    {
                        this_response = response;
                        last_response_len = response.length;
                    }
                    else
                    {
                        this_response = response.substring(last_response_len);
                        last_response_len = response.length;
                    }
                    console.log(this_response);
                }
            }
        })
        .done(function(data)
        {
            console.log('Complete response = ' + data);
        })
        .fail(function(data)
        {
            console.log('Error: ', data);
        });
        console.log('Request Sent');
        </script>
    </body>
</html>

JSONでこれを行う必要がある場合はどうなりますか?

完全なオブジェクトを取得するまで構文は常に無効になるため、(完全にロードされる前に)単一のJSONオブジェクトを段階的にロードすることは実際には不可能です。

ただし、応答に複数のJSONオブジェクトが次々に含まれている場合は、パイプを下って来るときに一度に1つずつロードすることができます。

だから私は上記のコードを微調整しました...

  1. PHPFILEの4行目をからecho $val;に変更しecho '{"name":"'.$val.'"};'ます。これにより、一連のJSONオブジェクトが出力されます。

  2. HTMLファイルの24行目をからconsole.log(this_response);に変更

    this_response = JSON.parse(this_response);
    console.log(this_response.name);
    

    この基本的なコードは、ブラウザーに到達する各「チャンク」が有効なJSONオブジェクトであることを前提としていることに注意してください。パケットがどのように到着するかを予測できないため、これが常に当てはまるとは限りません。セミコロンに基づいて文字列を分割する必要がある場合があります(または別の区切り文字を考え出す必要があります)。

使用しないでくださいapplication/json

ヘッダーを次のように変更しないでくださいapplication/json-私はこれを行い、3日間グーグルで検索しました。応答タイプがのapplication/json場合、ブラウザは、完全に完了した場合と同様に、応答が完了するまで待機します。次に、完全な応答が解析され、実際にJSONであるかどうかが確認されます。ただし、完全な応答は{...};{...};{...};、有効なJSONではありません。jqXHR.done完全な応答をJSONとして解析できないため、このメソッドはエラーが発生したと想定します。

コメントで述べたように、次を使用してクライアント側でこのチェックを無効にすることができます。

$.ajax(..., {dataType: "text"})

一部の人々がこれが役立つことを願っています。

于 2013-09-23T16:17:54.417 に答える
34

XMLHttpRequest.jsを使用する

https://github.com/ilinsky/xmlhttprequest

http://code.google.com/p/xmlhttprequest

  • XMLHttpRequest 1.0オブジェクトの目立たない標準準拠(W3C)クロスブラウザー実装を提供します
  • ネイティブのXMLHttpRequestオブジェクトの実装で観察されたすべてのブラウザーの癖を修正します
  • XMLHttpRequestオブジェクトアクティビティの透過的なロギングを有効にします

PHPでロングポーリングを使用するには:

output.php:

<?php
header('Content-type: application/octet-stream');

// Turn off output buffering
ini_set('output_buffering', 'off');
// Turn off PHP output compression
ini_set('zlib.output_compression', false);
// Implicitly flush the buffer(s)
ini_set('implicit_flush', true);
ob_implicit_flush(true);
// Clear, and turn off output buffering
while (ob_get_level() > 0) {
    // Get the curent level
    $level = ob_get_level();
    // End the buffering
    ob_end_clean();
    // If the current level has not changed, abort
    if (ob_get_level() == $level) break;
}
// Disable apache output buffering/compression
if (function_exists('apache_setenv')) {
    apache_setenv('no-gzip', '1');
    apache_setenv('dont-vary', '1');
}

// Count to 20, outputting each second
for ($i = 0;$i < 20; $i++) {
    echo $i.str_repeat(' ', 2048).PHP_EOL;
    flush();
    sleep(1);
}

run.php:

<script src="http://code.jquery.com/jquery-1.6.4.js"></script>
<script src="https://raw.github.com/ilinsky/xmlhttprequest/master/XMLHttpRequest.js"></script>

<script>
$(function() {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', '/longpoll/', true);
    xhr.send(null);
    var timer;
    timer = window.setInterval(function() {
        if (xhr.readyState == XMLHttpRequest.DONE) {
            window.clearTimeout(timer);
            $('body').append('done <br />');
        }
        $('body').append('state: ' + xhr.readyState + '<br />');
        console.log(xhr.responseText);
        $('body').append('data: ' + xhr.responseText + '<br />');
    }, 1000);
});
</script>

これは出力するはずです:

state: 3
data: 0
state: 3
data: 0 1
state: 3
data: 0 1 2
state: 3
data: 0 1 2 3
state: 3
data: 0 1 2 3 4
...
...
...
state: 3
data: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
state: 3
data: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
state: 3
data: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
done
state: 4
data: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 

IEの場合、XDomainRequestを調べる必要があります

http://blogs.msdn.com/b/ieinternals/archive/2010/04/06/comet-streaming-in-internet-explorer-with-xmlhttprequest-and-xdomainrequest.aspx

http://msdn.microsoft.com/en-us/library/cc288060(VS.85).aspx

于 2011-11-02T02:30:28.170 に答える
21

これには、まっすぐなJavaScriptを使用する必要があります。その理由は、コールバックが発生するのを待たずに、継続的にポーリングする必要があるためです。これにはjQueryは必要ありません。非常に簡単です。Ajax PatternsのWebサイトに、このための優れたソースコードがいくつかあります。

基本的には、応答の最後の位置を追跡し、その場所を超えてさらに多くのテキストを定期的にポーリングする必要があります。あなたの場合の違いは、あなたが完全なイベントを購読してあなたのポーリングを止めることができるということです。

于 2011-10-12T13:31:15.467 に答える
16

サーバーはストリームフレンドリー(非同期)であり、jqueryソリューションを探していたとのことですが、jQuery Stream Pluginをチェックアウトしましたか?

使い方はとても簡単で、何も心配する必要はありません。かなり良い ドキュメントもあります。

于 2011-11-01T12:12:52.313 に答える
1

最大許容サイズ制限まで実行し続ける大きなJSONペイロードをグリッドに提供する必要がありました。私はMVCとjqueryを使用していたので、上記のAlexMorley-Finchのソリューションを採用しました。

サーバーコードは 「WebAPIを使用したデータのストリーミング」からのものです。また、https://github.com/DblV/StreamingWebApi

public class StreamingController : ApiController
{

    [HttpGet]
    [ActionName("GetGridDataStream")]
    public HttpResponseMessage GetGridDataStream(string id)
    {
        var response = Request.CreateResponse();
        DynamicData newData = new DynamicData();
        var res = newData.GetDataRows(id);
        response.Content = new PushStreamContent((stream, content, context) =>
        { 
            foreach (var record in res)
            {
                var serializer = new JsonSerializer();
                using (var writer = new StreamWriter(stream))
                {
                    serializer.Serialize(writer, record);
                    stream.Flush();
                }

               // Thread.Sleep(100);
            }

            stream.Close();
        });

        return response;
    }
}

これにより、{json object} {json object} {json object}のストリームが作成され、jsonとして正常に解析されるためにコンマと周囲の[]を区切る必要がありました。

クライアントコードには、不足している文字が次のように提供されました。

 var jsonData = {}; 

 $.ajax("api/Streaming/GetGridDataStream/" + viewName, {
    xhrFields: {
            onprogress: function (e) { 
                // console.log(this_response);
            }
        }
    }, { dataType: "text" }) //<== this is important for JSON data
    .done(function (data) { 

        data = "[" + data.replace(/\}\{/gi, "},{") + "]";

        jsonData["DataList"] = JSON.parse(data);
        //more code follows to create grid
    })
    .fail(function (data) {
        console.log('Error: ', data);
    });

これが.NetMVCとjQueryを使用している人に役立つことを願っています。

于 2019-12-10T02:30:22.250 に答える
0

JQueryを使用してこれを実現する簡単な方法は次のとおりです(OPの要求に応じて)。

まず、 https: //gist.github.com/chrishow/3023092 (この応答の下部に追加)から以下のコードを実行して、ajaxオブジェクトを拡張してonreadystatechangeをサポートします。次に、xhr.responseTextで新しいテキストをチェックするonreadystatechange関数を使用してajaxを呼び出すだけです。

さらに詳しく知りたい場合は、ここで説明するように、読み取るたびにresponseTextデータをクリアできます)。

たとえば、https://jsfiddle.net/g1jmwcmw/1/を参照してください。これにより、 https://code.jquery.com/jquery-1.5.jsから応答がダウンロードされ、コンソールウィンドウ内にチャンクで出力されます。以下のコード(htmlページにコピーしてブラウザで開くことができます):

<!-- jquery >= 1.5. maybe earlier too but not sure -->
<script src=https://code.jquery.com/jquery-1.5.min.js></script>
<script>
/* One-time setup (run once before other code)
 *   adds onreadystatechange to $.ajax options
 *   from https://gist.github.com/chrishow/3023092)
 *   success etc will still fire if provided
 */
$.ajaxPrefilter(function( options, originalOptions, jqXHR ) {
    if ( options.onreadystatechange ) {
        var xhrFactory = options.xhr;
        options.xhr = function() {
            var xhr = xhrFactory.apply( this, arguments );
            function handler() {
                options.onreadystatechange( xhr, jqXHR );
            }
            if ( xhr.addEventListener ) {
                xhr.addEventListener( "readystatechange", handler, false );
            } else {
                setTimeout( function() {
                    var internal = xhr.onreadystatechange;
                    if ( internal ) {
                        xhr.onreadystatechange = function() {
                            handler();
                            internal.apply( this, arguments ); 
                        };
                    }
                }, 0 );
            }
            return xhr;
        };
    }
});

// ----- myReadyStateChange(): this will do my incremental processing -----
var last_start = 0; // using global var for over-simplified example
function myReadyStateChange(xhr /*, jqxhr */) {
    if(xhr.readyState >= 3 && xhr.responseText.length > last_start) {
        var chunk = xhr.responseText.slice(last_start);
        alert('Got chunk: ' + chunk);
        console.log('Got chunk: ', chunk);
        last_start += chunk.length;
    }
}

// ----- call my url and process response incrementally -----
last_start = 0;
$.ajax({
  url: "https://code.jquery.com/jquery-1.5.js", // whatever your target url is goes here
  onreadystatechange: myReadyStateChange
});

</script>
于 2017-11-13T05:56:15.187 に答える