в jQuery Аякс трансляция читать последовательно?


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

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

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

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

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

5 65

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); до

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

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

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

Do не для изменения заголовков на application/json - Я сделал это, и это было мне Гуглить в течение 3 дней. Когда тип ответа application/json, браузер ждет, пока ответ не будет завершен, как в полностью завершена. Затем полный ответ анализируется, чтобы проверить, является ли он infact JSON. Однако наш полный ответ {...};{...};{...}; который не является допустимым JSON. Элемент 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);
}

выполнить.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>