XmlHttpRequest.responseText при загрузке (readyState == 3) в Chrome - PullRequest
7 голосов
/ 07 октября 2010

Я пытаюсь выполнить «потоковую передачу» (с сервера на клиент) в Javascript с помощью ajax (с помощью XmlHttpRequest (= xhr). Я использую модифицированную функцию handleResponse, описанную в Кросс-браузерная реализация «HTTP Streaming» (push) Шаблон AJAX

function handleResponse() {
if (http.readyState != 4 && http.readyState != 3)
    return;
if (http.readyState == 3 && http.status != 200)
    return;
if (http.readyState == 4 && http.status != 200) {
    clearInterval(pollTimer);
    inProgress = false;
}
// In konqueror http.responseText is sometimes null here...
if (http.responseText === null)
    return;

while (prevDataLength != http.responseText.length) {
    if (http.readyState == 4  && prevDataLength == http.responseText.length)
        break;
    prevDataLength = http.responseText.length;
    var response = http.responseText.substring(nextLine);
    var lines = response.split('\n');
    nextLine = nextLine + response.lastIndexOf('\n') + 1;
    if (response[response.length-1] != '\n')
        lines.pop();

    for (var i = 0; i < lines.length; i++) {
        // ...
    }
}

if (http.readyState == 4 && prevDataLength == http.responseText.length)
    clearInterval(pollTimer);

inProgress = false;
}

С помощью php-скрипта, который сбрасывает мне данные (без ajax он действительно сбрасывает данные в браузер во время выполнения)

У меня нет проблем в Firefox, но GoogleChrome и IE дают мне пустой responseText, тогда как xhr.readyState равен 3. Я нашел эту проблему, описанную в Интернете, но она не дала мне никакого решения.

Знаете ли вы, как пройти мимо этогопроблема реализации в Chrome? (w3c говорит, что responseText не может быть NULL в readyState == 3 - Chrome реализовал это правило, но выдает только пустую строку)

А если вы не знаете, знаете ли выкакое-либо рабочее решение в некоторых продуктах? (фреймворки с открытым исходным кодом, библиотеки и т. д.)

Большое спасибо за ваши идеи.

Редактировать: Обходной путьпри создании iframe вызывайте скрипт для iframe, сбрасывайте здесь данные и извлекайте данные с помощью javascript из iframe.Но это не решение Ajax.Я действительно хотел бы видеть чистое решение ajax.

Ответы [ 11 ]

17 голосов
/ 21 января 2011

В Chrome есть ошибка, из-за которой он заполняет xhr.responseText только после получения определенного количества байтов.Есть 2 способа обойти это,

Установить тип содержимого возврата «application / octet-stream»

или

Отправить прелюдию размером около 2 КБ дляподготовьте обработчик.

Любой из этих методов должен заставить chrome заполнять поле responseText, когда readyState == 3.

IE7 / 8, с другой стороны, не может этого сделать, вам нужно прибегнуть кдля продолжительного опроса или использования междоменного трюка с XDomainRequest в IE8, например MS

3 голосов
/ 26 февраля 2014

Чтобы расширить ответ Эндрю, это кросс-браузерное решение, которое я придумал.

Работает правильно в 99% браузеров, а именно IE ≥ 8, Chrome, Firefox и Safari , отправка добавочных событий, как только данные получены браузером (но см. Примечания ниже).

if (/MSIE [8-9]/.test(navigator.appVersion)) {
    var get = new XDomainRequest()
    get.onprogress = handleData
    get.onload = handleData
} else {
    var get = new XMLHttpRequest()
    get.onreadystatechange = handleData
}
get.open('get', '/example/url')
get.send()

function handleData() {
    if (get.readyState != null && (get.readyState < 3 || get.status != 200)) {
        return
    }
    // process incremental data found in get.responseText
}

IE 8–9 начнет заполнять responseText после 2 КБ данных, так что если это не нормально, вы должны отправить начальный отступ 2 КБ.

Chrome нуждается либо в этом, либо в Content-Type: application/octet-stream.

3 голосов
/ 12 октября 2010

Рассматривали ли вы использование WebSockets или отправленных на сервер событий ?

Большинство основных браузеров теперь поддерживают протокол WebSocket, хотя, если ваш сайт должен работать в IE 9 или более ранней версии, или в Android Browser 4.3 или более ранней версии, вам придется сохранить код, который использует XMLHttpRequest, как запасной вариант.

Большинство этих браузеров также поддерживают функцию, называемую событиями, отправляемыми на сервер , которые, в отличие от WebSockets, могут быть реализованы на сервере с использованием традиционного демона HTTP и сценария CGI / PHP хотя обеспечивает только одностороннюю связь.

См. Также: WebSockets против событий, отправленных сервером / EventSource

2 голосов
/ 16 октября 2010

К сожалению, каждая часть XmlHttpRequest (или любых веб-стандартов) не полностью реализована во всех браузерах. Но у вас есть несколько других опций для потоковой передачи HTTP:
Википедия: Технология Push
Википедия: Комета (программирование) Википедия: веб-сокеты (экспериментальная, слабая поддержка браузера)

Я видел в вашем комментарии, что вы хотели бы, чтобы это был чистый AJAX, но я хотел бы предложить возможные альтернативные пути решения. Вы можете использовать JavaApplet, где это возможно, или Flash-объект. Для последнего вам не понадобится роскошная и дорогая IDE, вы можете использовать Haxe для создания Flash / SWF-файлов, и вы будете чувствовать себя довольно комфортно с ней, как вы знаете JavaScript.

Вот пример чата Flash / Neko , который, вероятно, может быть адаптирован и для других платформ и для использования.

Желаю вам удачи.

1 голос
/ 29 августа 2011

Установка типа содержимого возврата в «application / octet-stream», как предложил Эндрю, была отличным решением.Кроме того, вы должны использовать XDomainRequest в IE.

Чтобы читать данные по мере их поступления, вы должны просто использовать бесконечный цикл (который останавливается, когда вызывается readystate = 4 или XDomainRequest.onLoad) с таймаутом.

Вот как я бы это сделал:

var i = 0;
var returnValue = function() {
    if (!finished) {
        setTimeout(returnValue, 100);
    }
    var resp = ajax.responseText;
    var newI = resp.lastIndexOf(";") + 1;
    if (newI > i) {
        var lines = resp.substring(i, newI).split(";");
        for (var x = 0; x < lines.length; x++) {
            eval(lines[x]);
        }
        i = newI;
    }
}

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

1 голос
/ 21 октября 2010

Как сказал Ярослав Моравец, если вы установите тип содержимого в заголовке потока на application / x-javascript, он работает в Safari, Chrome и Firefox.

Я не проверял IE.

1 голос
/ 18 октября 2010

Это работало для меня для Chrome, но не для IE:

[test.php]:

<?php
Header('Content-type: text/plain');
while (1) {
    echo str_pad('test: '.mt_rand(1000,9999), 2048, ' ');
    flush();
    sleep(1);
}

[test.html]:

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Stream test</title>
        <meta charset="UTF-8" />
        <script type="text/javascript">
            function xmlHttpRequest() {
                return (function (x,y,i) {
                    if (x) return new x();
                    for (i=0; i<y.length; y++) try { 
                    return new ActiveXObject(y[i]);
                    } catch (e) {}
                })(
                    window.XMLHttpRequest, 
                    ['Msxml2.XMLHTTP','Microsoft.XMLHTTP']
                );
            };
            function stream(url) {
                // Declare the variables we'll be using
                var xmlHttp = xmlHttpRequest();
                xmlHttp.open("GET", url, true);
                var len = 0;
                xmlHttp.onreadystatechange = function() {
                if (xmlHttp.status == 200 && xmlHttp.readyState >=3) {
                    var text = xmlHttp.responseText;
                    text = text.substr(len, text.length-len);
                    len = xmlHttp.responseText.length;
                    console.log(text);
                }
                }
                xmlHttp.send(null);
            }           
            stream('/test.php');
        </script>
    </head>
    <body>
    </body>
</html>

YMMV.

1 голос
/ 15 октября 2010

Насколько я понимаю, сделать частичный текст доступным на readyState 3 - это нестандартное поведение только для Firefox, которое просто невозможно напрямую эмулировать в других браузерах. Вместо этого вам может потребоваться сделать несколько последовательных запросов для небольших кусков. данных, а не один потоковый запрос

1 голос
/ 13 октября 2010

попробуйте использовать свойство responseStream / responseBody в IE. Я думал о том, чтобы сделать подобное, и столкнулся с той же проблемой. К сожалению, ни w3c спецификации

http://www.w3.org/TR/XMLHttpRequest/#the-responsetext-attribute

0 голосов
/ 11 октября 2010

однажды у меня возникла эта проблема с использованием safari (никогда не тестировалась с chrome, может быть, была одна и та же проблема (chrome / safari оба используют один и тот же движок рендеринга (насколько я знаю) - не знаю о js-parts))).Я никогда не находил решения, чтобы обойти это, но из-за того, что это было маленькое приложение в корпоративной сети, было не проблема не поддерживать Safari (ff был браузером по умолчанию, и ff работает).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...