jquery ajax,逐步读取stream?

我已经读过这个问题,但并不完全回答我的问题。 不幸的是,自从我上次查看Ajax以来,XHR对象中的内容看起来已经发生了变化,因此在完成填充之前不能再直接访问responseText
我必须编写一个页面,使用AJAX(最好是jQuery,但我愿意build议)通过HTTP从我无法控制的服务器检索CSV数据。 响应数据可能相当大; 一个兆字节的文本并不less见。
服务器是stream的友好的。 还有什么方法可以直接从Javascript返回数据stream吗?
我可以select编写一些居于中间的PHP代码,并使用某种“Comet”技术(长轮询,EventSource等),但是如果可能,我宁愿避免这种情况。

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

你会想直接使用JavaScript来做到这一点。 原因是你要继续投票,而不是等待callback。 你不需要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对象,因为直到拥有完整的对象,语法将始终是无效的。

但是,如果您的响应具有多个 JSON对象,那么可以一次加载一个JSON对象,因为它们沿着pipe道向下。

所以我调整了我的代码上面…

  1. 更改PHP文件行4从echo $val; echo '{"name":"'.$val.'"};' 。 这会输出一系列的JSON对象。

  2. console.log(this_response);更改HTML FILE行24 console.log(this_response);

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

    请注意,这个基本的代码假定每个“块”到浏览器是一个有效的JSON对象。 这并不总是如此,因为您无法预测数据包将如何到达 – 您可能需要根据分号分割string(或提出另一个分隔符)。

不要使用application/json

不要为了改变你的头到application/json – 我做了这个,它让我谷歌search3天。 当响应types是application/json ,浏览器等待响应完成,如完整完成。 然后parsing完整的响应来检查它是否是事实上的JSON。 然而,我们的完整回复是{...};{...};{...}; 这是不正确的JSON。 jqXHR.done方法假定有一个错误,因为完整的响应不能被parsing为JSON。

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

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

希望有些人觉得这有用。

使用XMLHttpRequest.js

https://github.com/ilinsky/xmlhttprequest

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

  • 提供XMLHttpRequest 1.0对象的不引人注目的符合标准(W3C)的跨浏览器实现
  • 修复了在本机XMLHttpRequest对象实现中观察到的所有浏览器怪癖
  • 启用XMLHttpRequest对象活动的透明logging

要在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.php:

 <script src="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

既然你说你的服务器是stream的(asynchronous),并且正在寻找一个jQuery解决scheme,你检查了jQuery Stream Plugin吗?

它使用起来非常简单,使您不必担心任何事情。 它也有很好的 文档 。

以下是使用JQuery实现这一点的简单方法(按照OP的要求):

首先,通过运行https://gist.github.com/chrishow/3023092下面的代码(附加在这个响应的底部)来扩展ajax对象以支持onreadystatechange。; 然后使用onreadystatechange函数调用ajax,它将检查xhr.responseText是否为新文本。

如果你想更有趣,可以在每次阅读时清除responseText数据,如这里所述)。

例如:

 <!-- jquery >= 1.5. maybe earlier too but not sure --> <script src=http://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); console.log('Got chunk: ', chunk); last_start += chunk.length; } } // ----- call my url and process response incrementally ----- last_start = 0; $.ajax({ url: "http://mockbin.com/request?foo=bar&foo=baz", onreadystatechange: myReadyStateChange }); </script>