[jquery] jQuery는 AJAX 스트림을 점진적으로 읽습니까?

이 질문 을 읽었 지만 내 질문에 정확히 대답하지 않습니다. 불행히도 AJAX를 마지막으로 살펴본 이후로 XHR 개체에서 상황이 변경된 것처럼 보이므로 responseText채워지기 전에 더 이상 직접 액세스 할 수 없습니다 .

내가 제어 할 수없는 서버에서 HTTP를 통해 CSV 데이터를 검색하려면 AJAX (가급적 jQuery가 좋지만 제안에 개방적 임)를 사용하는 페이지를 작성해야합니다. 응답 데이터는 상당히 클 수 있습니다. 메가 바이트의 텍스트는 드문 일이 아닙니다.

서버는 스트림 친화적입니다. JavaScript에서 직접 반환되는 데이터 스트림에 액세스 할 수있는 방법이 있습니까?

중간에 있고 일종의 “Comet”기술 (긴 폴링, EventSource 등)을 사용하는 PHP 코드를 작성할 수있는 옵션이 있지만 가능하면 피하는 것이 좋습니다.

관련이있는 경우이 질문에 대해 사용자가 최신 버전의 Firefox / Chrome / Opera를 사용하고 있으며 이전 브라우저 호환성은 문제가되지 않는다고 가정합니다.



답변

이를 위해 자바 스크립트를 바로 사용하고 싶을 것입니다. 그 이유는 콜백이 실행될 때까지 기다리지 않고 지속적으로 폴링하기를 원하기 때문입니다. 이를 위해 jQuery가 필요하지 않으며 매우 간단합니다. 그들은 몇 가지가 아약스 패턴 웹 사이트에서 이것에 대한 좋은 소스 코드를 .

기본적으로 응답에서 마지막 위치를 추적하고 해당 위치를지나 더 많은 텍스트를 주기적으로 폴링하기를 원할 것입니다. 귀하의 경우 차이점은 전체 이벤트를 구독하고 폴링을 중지 할 수 있다는 것입니다.


답변

이것은 텍스트 나 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. PHP FILE 라인 4를 echo $val;에서 echo '{"name":"'.$val.'"};'. 일련의 JSON 객체가 출력됩니다.

  2. HTML FILE 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인지 확인합니다. 그러나 우리의 FULL 응답은 {...};{...};{...};유효한 JSON이 아닙니다. 이 jqXHR.done메서드는 전체 응답을 JSON으로 구문 분석 할 수 없기 때문에 오류가 있다고 가정합니다.

주석에서 언급했듯이 다음을 사용하여 클라이언트 측에서이 검사를 비활성화 할 수 있습니다.

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

어떤 사람들이 이것이 유용하다고 생각하기를 바랍니다.


답변

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


답변

서버가 스트림 친화적 (비동기)이라고 말하고 jquery 솔루션을 찾고 있었으므로 jQuery Stream Plugin을 확인 하셨습니까?

정말 사용하기 쉽고 아무것도 걱정할 필요가 없습니다. 그것은 또한 꽤 좋은 문서 를 가지고 있습니다.


답변

다음은 JQuery를 사용하여이를 달성하는 간단한 방법입니다 (OP에서 요청한대로).

먼저 https://gist.github.com/chrishow/3023092 (이 응답 하단에 추가됨)에서 아래 코드를 실행하여 onreadystatechange를 지원하도록 ajax 객체를 확장합니다 . 그런 다음 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>


답변

최대 허용 크기 제한까지 계속 실행되는 대규모 JSON 페이로드를 그리드에 제공해야했습니다. 저는 MVC와 jquery를 사용하고 있었기 때문에 위의 AlexMorley-Finch 솔루션을 적용했습니다.

서버 코드는 “웹 API를 사용하여 데이터 스트리밍” 에서 가져 왔습니다 . 또한 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으로 성공적으로 구문 분석해야하는 {json object} {json object} {json object} 스트림이 생성되었습니다.

따라서 클라이언트 코드에 누락 된 문자가 제공되었습니다.

 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);
    });

.Net MVC 및 jQuery를 사용하는 사람에게 도움이되기를 바랍니다.


답변