jQuery читает поток AJAX постепенно?

Я читал этот вопрос но это не ответ на мой вопрос. К сожалению, похоже, что все изменилось в объекте XHR с тех пор, как я последний раз смотрел на AJAX, поэтому прямой доступ к responseText прежде чем он будет заполнен.

Я должен написать страницу, которая использует AJAX (предпочтительно jQuery, но я открыт для предложений) для получения данных CSV через HTTP с сервера, над которым у меня нет контроля. Данные ответа могут быть вполне большой; мегабайт текста не редкость.

сервер является потоковым. Есть ли еще способ получить доступ к потоку данных по мере его возврата непосредственно из JavaScript?

У меня есть возможность написать некоторый PHP-код, который живет посередине и использует какую-то технологию "кометы" (длинный опрос, EventSource и т. д.), Но я бы предпочел избежать этого, если это возможно.

если это актуально, предположим, что для этого вопроса у пользователей есть последняя версия Firefox / Chrome / Opera и старой совместимости браузера не является проблемой.

5 ответов


вы захотите использовать для этого прямой javascript. Причина в том, что вы захотите постоянно опрашивать и не ждать, пока обратные вызовы будут срабатывать. Для этого вам не нужен jQuery, это довольно просто. У них есть некоторые хороший исходный код для этого на веб-сайте Ajax Patterns.

по сути, вы просто хотите отслеживать свою последнюю позицию в ответе и периодически опрашивать больше текста за этим местоположением. Разница в вашем случае это то, что вы можете подписаться на полное событие и остановить свой опрос.


это довольно просто при выводе текст или 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 4 из echo $val; до echo '{"name":"'.$val.'"};'. Это выводит ряд объектов JSON.

  2. изменение строки файла HTML 24 из console.log(this_response); to

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

    обратите внимание, что этот рудиментарный код предполагает, что каждый "кусок", поступающий в браузер, является допустимым объектом JSON. Это не всегда будет так, потому что невозможно предсказать, как пакеты будут приходить - вам может понадобиться разделить строку на основе точки с запятой (или придумать другой разделитель).

не используйте application/json

Do не для изменения заголовков на application/json - Я сделал это, и у меня был я Погуглите на 3 дня. Когда тип ответа application/json, браузер ждет, пока ответ не будет завершен, как в полностью завершено. Затем полный ответ анализируется, чтобы проверить, является ли он infact JSON. Однако наш полный ответ {...};{...};{...}; который не является допустимым JSON. The jqXHR.done метод предполагает, что произошла ошибка, потому что полный ответ не может быть проанализирован как JSON.

как упоминалось в комментариях, вы можете отключить эту проверку на стороне клиента использование:

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

надеюсь, некоторые люди считают это полезным.


Используйте XMLHttpRequest.js

https://github.com/ilinsky/xmlhttprequest

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

  • обеспечивает ненавязчивую стандартную совместимую (W3C) кросс-браузерную реализацию объекта XMLHttpRequest 1.0
  • исправляет все причуды браузеров, наблюдаемые в их собственных реализациях объектов XMLHttpRequest
  • включает прозрачное ведение журнала объекта XMLHttpRequest активность

использовать длинный опрос с PHP:

выход.на 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?

Он очень прост в использовании и позволяет вам не беспокоиться о многом. Он имеет очень хорошо документация Как хорошо.


вот простой способ достичь этого с помощью JQuery (по запросу OP):

во-первых, расширьте объект ajax для поддержки onreadystatechange, выполнив приведенный ниже код изhttps://gist.github.com/chrishow/3023092 (прилагается в нижней части этого ответа). Затем просто вызовите ajax, используя функцию onreadystatechange, которая проверит xhr.responseText для нового текста.

Если вы хотите получить еще больше, вы можете очистить данные 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>