jQuery逐步读取AJAX流?

Jos*_*osh 70 ajax jquery javascript-events long-polling http-streaming

我已经阅读了这个问题,但它并没有完全回答我的问题.不幸的是,自从我上次查看AJAX以来,看起来XHR对象中的内容发生了变化,因此responseText在完成填充之前不再可以直接访问.

我必须编写一个使用AJAX(最好是jQuery,但我愿意接受建议)的页面来从我无法控制的服务器通过HTTP检索CSV数据.响应数据可能非常大; 一兆字节的文本并不少见.

服务器是流友好的.是否有任何方法可以直接从JavaScript返回数据流?

我可以选择编写一些生活在中间的PHP代码并使用某种"Comet"技术(长轮询,EventSource等),但我希望尽可能避免这种情况.

如果它是相关的,假设这个问题用户有最新版本的Firefox/Chrome/Opera和旧的浏览器兼容性不是问题.

Ale*_*nch 66

输出文本或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...');
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

如果我需要使用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);
    
    Run Code Online (Sandbox Code Playgroud)

    请注意,此基本代码假定进入浏览器的每个"块"都是有效的JSON对象.情况并非总是如此,因为您无法预测数据包将如何到达 - 您可能需要根据分号分割字符串(或者提出另一个分隔符字符).

不要用 application/json

难道不是为了改变你的头来application/json-我做了这一点,它让我谷歌搜索3天.当响应类型application/json为时,浏览器会等待响应完成,如完全完成.然后解析完整响应以检查它是否是实际的JSON.但是我们的FULL响应是{...};{...};{...};无效的JSON.该jqXHR.done方法假定存在错误,因为无法将完整响应解析为JSON.

如评论中所述,您可以使用以下命令在客户端禁用此检查:

$.ajax(..., {dataType: "text"})
Run Code Online (Sandbox Code Playgroud)

希望有些人觉得这很有用.

  • 非常感谢,这花了我1分钟成功实施.好东西. (3认同)
  • 关于 PHP 的注释:通过连接字符串在 PHP 端手动创建 JSON 通常是不好的做法(例如 `echo '{"name":"'.$val.'"};'`)。一些更好的代码可能是 `echo json_encode(["name"=&gt;$val]).";";`。 (2认同)

Pet*_*tah 32

使用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 Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

这应输出:

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 
Run Code Online (Sandbox Code Playgroud)

对于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

  • @Bakalash,因为有些浏览器在发送2kb输出之前不允许流式传输. (3认同)

sco*_*kel 20

你会想要直接使用javascript.原因是你想要不断轮询而不是等待回调.你不需要jQuery,这很简单.他们在Ajax Patterns网站上有一些很好的源代码.

从本质上讲,您只需要跟踪响应中的最后位置,并定期轮询该位置之外的更多文本.您的情况有所不同,您可以订阅完整的活动并停止投票.

  • 你能指点我一个有效的例子吗?您提供的链接表示"XMLHttpRequest的responseText属性始终包含已从服务器刷新的内容,即使连接仍处于打开状态." ..而且,从我一直在阅读的内容来看,在较新的浏览器中不再是这种情况. (3认同)

g19*_*tic 16

既然你说你的服务器是流友好的(异步)并且正在寻找一个jquery解决方案,你有没有检查过jQuery Stream插件

它非常易于使用,让您不必担心任何事情.它也有很好的 文档.

  • +1它现在变成了门户网站,它看起来非常棒,包含了WebSockets和所有内容.https://github.com/flowersinthesand/portal (5认同)