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


78

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

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

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

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

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


나는 이것이 대답되었다는 것을 알고있다, 나는 전에 이와 같은 것을했다, 당신이 필요
MrJD

답변:


21

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

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


3
실제 사례를 알려 주시겠습니까? 귀하가 제공 한 링크에는 "XMLHttpRequest의 responseText 속성에는 항상 연결이 열려있는 경우에도 서버에서 플러시 된 콘텐츠가 포함되어 있습니다." .. 그리고 제가 읽은 바에 따르면 이것은 더 이상 최신 브라우저에서는 해당되지 않습니다.
조쉬

IE에만 있지 않습니까? readyState 3에는 다른 브라우저에 포함되어 있다고 생각했습니다.
scottheckel

1
주로 나는이 jquery 플러그인 의 NOTE를 보았습니다 : plugins.jquery.com/project/ajax-http-stream '참고 : 이것이 Firefox 3.0.11 (3.0.11에서 작동)에서 더 이상 작동하지 않는다는 것이 제 관심을 끌었습니다. 8), IE8 또는 최신 버전의 Chrome. 분명히 추세는 요청이 완료되기 전에 xmlhttprequest.responseText에 대한 액세스를 허용하지 않는 것입니다 (멍청한 imo). 죄송합니다
Josh

이것은 실제로 시도한 후 (적어도 제대로 작동하는 브라우저에서) 직선 자바 스크립트에서 작동하는 것으로 나타났습니다. 모든 브라우저에서 올바르게 작동하도록 jquery 버전을 찾고 있지만 현재로서는 이것이 최선의 답변입니다.
Josh

죽은 링크는 나를 슬프게합니다
captncraig

77

이것은 텍스트 나 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"})

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


1
와우 감사합니다. 이것이 바로 제가 찾고 있던 것이 었습니다! 이 기술을 JSON과 함께 사용하는 방법에 대한 아주 좋은 예입니다.
아론

3
감사합니다. 성공적으로 구현하는 데 1 분이 걸렸습니다. 훌륭한 물건.
Pål Thingbø 2014

1
{dataType와 : "텍스트"}와 $ 아약스 호출이 지능형 추측을 억제한다 (참조 api.jquery.com/jquery.ajax 데이터 형식)
크리스토프 Quintard

1
예, oboe ( oboejs.com ) 와 같은 스트리밍 JSON 파서를 사용하여 JSON을 점진적으로 읽을 수 있습니다 . 여러 개의 JSON 개체를 갖도록 JSON 응답을 변경할 필요가 없으며 디자인 관점에서 보지 않는 것이 좋습니다
mwag

1
PHP에 대한 참고 사항 : 문자열을 연결하여 (예 :) PHP 쪽에서 수동으로 JSON을 만드는 것은 일반적으로 나쁜 습관 echo '{"name":"'.$val.'"};'입니다. 더 나은 코드는 echo json_encode(["name"=>$val]).";";.
Laef

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


이것은 Chrome에서도 지원되지 않는 readystate 3을 지원하는 것 같습니다. (
Josh

1
@Josh, 네 그렇습니다. 그러나 긴 폴링에는 여러 가지 단점이 있습니다. 읽기 상태가 변경되기 전에 2Kb의 데이터를 전송하고 콘텐츠 유형을로 설정해야합니다 application/octet-stream. PHP 예제는 업데이트 된 게시물을 참조하십시오.
Petah 2011

나는 이것으로 무엇을 할 수 있는지 볼 것이다. 원래 응답의 내용 유형을 제어 할 수 없기 때문에 중간에 PHP가 있어야하는 것은 불가피한 것 같습니다. 하지만 IE6 / 7도 지원하고 싶습니다 (불행히도) ...
Josh

1
@xorinzor pastebin.com/3Dbt2mhQ 하지만 필요에 따라 사용자 지정 프로토콜을 구현해야 할 수도 있습니다. 그런 다음 ;.
Petah

3
일부 브라우저는 2kb의 출력이 전송 될 때까지 스트리밍을 허용하지 않기 때문에 @Bakalash.
Petah

16

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

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


나는 이것을 확실히 볼 수있다. API 페이지의 빠른 훑어보기에서 HTTP POST 및 기본 인증 정보를 서버로 보내는 방법이 보이지 않지만 어딘가에 있어야합니다. 또한 "스트림 친화적"이 잘못된 용어 선택이었을 수도 있습니다. 나는 비동기 또는 양방향을 의미하지 않습니다. 나는 그것이 거대한 HTTP 응답과 같은 스트림으로 시간이 지남에 따라 많은 양의 데이터를 다시 전송한다는 것을 의미했습니다. 또한 원래 목적을 위해 "충분히 좋은"비 jquery 솔루션을 찾았습니다.
Josh

http 게시물 및 기본 인증의 경우 어쨌든 직선 jquery를 사용합니다.
g19fanatic

"어쨌든 직선 jquery"를 jquery 스트림 플러그인과 어떻게 통합합니까? 문서는 그 점에 대해 명확하지 않습니다. 예가 있습니까?
Josh 2011

5
+1 이제 포털로 바뀌었고 WebSocket과 모든 것을 포함하여 정말 멋져 보입니다. github.com/flowersinthesand/portal
marsbard

1
@marsbard 포털 의 수명 이 다하여 더 이상 유지 관리되지 않습니다! Vibe를 사용하십시오 .
김동환

0

다음은 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>

여기 OP. 이 질문은 6 년 전에 제기되었습니다. 이것이 2011/2012 년에 효과가 있었을까요? 나는 더 이상이 프로젝트에서 일하지 않기 때문에 당신의 대답을 테스트 할 수 없을 것입니다.
Josh

예, jquery 1.5 (2011 년 1 월, code.jquery.com/jquery-1.5.min.js )에서 잘 작동합니다 . 예를 들어 위의 코드를 잘라내거나 붙여 넣을 수 있습니다.
mwag

나는 당신을 믿어야합니다. 여러 브라우저에서 정확한 코드를 실행했고 전체 응답이 하나의 "청크"에 있었으므로 실제로 아무것도 증명하지 못했습니다. 더 이상 조작 할 시간이 없습니다.
Josh

당신은 그것을 볼 수 있어야합니다. 위의 내용을 그대로 test.html 파일에 저장하고 Chrome에서 열면 콘솔 창에 두 개의 청크로 수신 된 응답이 표시됩니다.
mwag

0

최대 허용 크기 제한까지 계속 실행되는 대규모 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를 사용하는 사람에게 도움이되기를 바랍니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.