【问题标题】:How to write javascript in client side to receive and parse `chunked` response in time?如何在客户端编写javascript以及时接收和解析`chunked`响应?
【发布时间】:2011-10-11 00:11:33
【问题描述】:

我正在使用播放框架来生成分块响应。代码是:

class Test extends Controller {
    public static void chunk() throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            String data = repeat("" + i, 1000);
            response.writeChunk(data);
            Thread.sleep(1000);
        }
    }
}

当我使用浏览器访问http://localhost:9000/test/chunk时,我可以看到显示的数据每秒都在增加。但是,当我编写一个 javascript 函数来接收和处理数据时,发现它会阻塞,直到收到所有数据。

代码是:

$(function(){
    $.ajax(
        "/test/chunked", 
        {
            "success": function(data, textStatus, xhr) {
                alert(textStatus);
            }
        }
    );
});

收到所有数据后,我可以看到一个消息框在 10 秒后弹出。

如何获取流并及时处理数据?

【问题讨论】:

    标签: ajax chunked


    【解决方案1】:

    success 事件将在完成数据传输并以 200 响应代码关闭连接时触发。我相信你应该能够实现一个原生的onreadystatechanged 事件并看到数据包的到来。

    【讨论】:

    • onreadystatechanged 只会在超时、中止和成功完成请求时触发。它不会在块上触发。
    • onreadystatechange 每次readystate 属性更改时都会被调用。
    【解决方案2】:

    jQuery 不支持,但你可以用普通的 XHR 做到这一点:

    var xhr = new XMLHttpRequest()
    xhr.open("GET", "/test/chunked", true)
    xhr.onprogress = function () {
      console.log("PROGRESS:", xhr.responseText)
    }
    xhr.send()
    

    这适用于all modern browsers,包括IE 10。W3C 规范here

    这里的缺点是xhr.responseText 包含累积响应。您可以在其上使用子字符串,但更好的做法是使用responseType 属性并在ArrayBuffer 上使用slice

    【讨论】:

    • 为什么使用 ArrayBuffer 更好?
    • @4esn0k:例如,如果您决定使用二进制协议,这是个好主意。这里的另一点是浏览器不必将整个数据保存在内存中,据我了解,使用ArrayBuffer 比使用String 更容易实现。 slice 应该工作得更好,因为它不必像 substring 那样使用 UTF-8 字符串,但可以只使用数组索引。
    • 是否有更结构化的方法/更好的库来做到这一点?
    • @Edmondo1984,我最终创建了这个包,它允许比 jsonpipe 更有效的传输:npmjs.com/package/chunked-request
    • 我正在尝试做与 OP 要求相同的事情。我按照菲尔的指示,得到了大块的回应。但是我应该怎么做才能不累积响应?我尝试将 responseType 设置为 arraybuffer,但没有得到任何响应。
    【解决方案3】:

    很快我们应该可以使用ReadableStream API (MDN docs here)。下面的代码似乎适用于 Chrome 版本 62.0.3202.94:

    fetch(url).then(function (response) {
        let reader = response.body.getReader();
        let decoder = new TextDecoder();
        return readData();
        function readData() {
            return reader.read().then(function ({value, done}) {
                let newData = decoder.decode(value, {stream: !done});
                console.log(newData);
                if (done) {
                    console.log('Stream complete');
                    return;
                }
                return readData();
            });
        }
    });
    

    【讨论】:

    • 以上代码运行良好。必须在“让 newData = 解码器”之前添加检查“值”不是未定义的。
    猜你喜欢
    • 2011-01-08
    • 2019-04-04
    • 1970-01-01
    • 1970-01-01
    • 2012-07-22
    • 1970-01-01
    • 2012-12-16
    • 1970-01-01
    相关资源
    最近更新 更多