jQuery以增量方式读取AJAX流?

jQuery以增量方式读取AJAX流?,jquery,ajax,dom-events,long-polling,http-streaming,Jquery,Ajax,Dom Events,Long Polling,Http Streaming,我读过,但它并没有完全回答我的问题。 不幸的是,自从我上次查看AJAX以来,XHR对象中的情况似乎发生了变化,因此在填充完成之前,无法再直接访问responseText 我必须编写一个页面,使用AJAX(最好是jQuery,但我愿意接受建议)通过HTTP从我无法控制的服务器检索CSV数据。响应数据可能相当大;兆字节的文本并不少见 服务器是流友好的。在返回数据流时,是否仍有直接从JavaScript访问数据流的方法 我确实可以选择写一些PHP代码,这些代码存在于中间,使用某种“慧星”技术(长轮询、

我读过,但它并没有完全回答我的问题。 不幸的是,自从我上次查看AJAX以来,XHR对象中的情况似乎发生了变化,因此在填充完成之前,无法再直接访问
responseText

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

服务器是流友好的。在返回数据流时,是否仍有直接从JavaScript访问数据流的方法

我确实可以选择写一些PHP代码,这些代码存在于中间,使用某种“慧星”技术(长轮询、EnvivStand等),但如果可能的话,我宁愿避免这样做。
如果与此相关,请假设用户拥有最新版本的Firefox/Chrome/Opera,并且旧浏览器兼容性不是问题。

您需要使用直接的javascript来解决此问题。原因是您希望不断进行轮询,而不是等待回调启动。这不需要jQuery,它非常简单。他们有一些


基本上,您只需要跟踪您在响应中的最后位置,并定期轮询超过该位置的更多文本。您案例中的不同之处在于,您可以订阅完整事件并停止轮询。

既然您说您的服务器是流友好型(异步)的,并且正在寻找jquery解决方案,您是否签出了

它真的很容易使用,让你不必担心任何事情。它也有。

使用XMLHttpRequest.js

  • 提供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);
}
对于IE,您需要查看XDomainRequest


当输出文本或HTML时,这非常简单。下面是一个例子

(但是,如果试图输出JSON,您会遇到问题,我将进一步解决这个问题。)

PHP文件 请注意,这段基本代码假定进入浏览器的每个“块”都是有效的JSON对象。这种情况并不总是如此,因为您无法预测数据包将如何到达-您可能需要根据分号拆分字符串(或使用另一个分隔符)

不要使用
application/json
不要将标题更改为
application/json
——我这样做了,它让我在谷歌上搜索了3天。当响应类型为
application/json
时,浏览器将等待响应完成,如完全完成。然后解析完整响应以检查它是否为infact JSON。然而,我们的完全回应是
{…};{...};{...};这不是有效的JSON。
jqXHR.done
方法假定存在错误,因为无法将完整响应解析为JSON

如注释中所述,您可以使用以下方法在客户端禁用此检查:

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

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

下面是使用JQuery实现这一点的简单方法(根据OP的要求):

首先,通过运行下面的代码(附加在响应的底部),扩展ajax对象以支持onreadystatechange。然后使用onreadystatechange函数调用ajax,该函数将检查xhr.responseText中的新文本

如果您想变得更有趣,可以在每次读取responseText数据时清除它,如所述)

例如,请参阅,它将使用以下代码从控制台窗口中下载响应并将其分块输出(您只需将代码复制到html页面,然后在浏览器中打开即可):


/*一次性设置(在其他代码之前运行一次)
*将onreadystatechange添加到$.ajax选项
*从https://gist.github.com/chrishow/3023092)
*如果提供,success etc仍将启动
*/
$.ajaxPrefilter(函数(选项、原始选项、jqXHR){
如果(options.onreadystatechange){
var xhrFactory=options.xhr;
options.xhr=函数(){
var xhr=xhrFactory.apply(这是参数);
函数处理程序(){
options.onreadystatechange(xhr、jqXHR);
}
if(xhr.addEventListener){
addEventListener(“readystatechange”,handler,false);
}否则{
setTimeout(函数(){
var internal=xhr.onreadystatechange;
国际单项体育联合会(内部){
xhr.onreadystatechange=函数(){
handler();
内部。应用(此,参数);
};
}
}, 0 );
}
返回xhr;
};
}
});
//----myReadyStateChange():这将执行增量处理-----
var last_start=0;//对过度简化的示例使用全局var
函数myReadyStateChange(xhr/*,jqxhr*/){
如果(xhr.readyState>=3&&xhr.responseText.length>last\u start){
var chunk=xhr.responseText.slice(最后一次启动);
警报('Got chunk:'+chunk);
log('Got chunk:',chunk);
last_start+=chunk.length;
}
}
//-----调用我的url并以增量方式处理响应-----
最后一次启动=0;
$.ajax({
url:“https://code.jquery.com/jquery-1.5.js“,//无论您的目标url是什么,都会显示在此处
onreadystatechange:myReadyStateChange
});

我必须为网格提供一个大的JSON负载,该负载在
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 
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...');
<!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>
this_response = JSON.parse(this_response);
console.log(this_response.name);
$.ajax(..., {dataType: "text"})
<!-- 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>
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;
    }
}
 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);
    });