分块数据传输无法正常工作

问题描述 投票:0回答:2

我正在使用 content-type:chunked 以块的形式传输数据,由 ,但是客户端接收到的数据并不是分块接收的,而是一起接收的。我该如何解决这个问题?

我目前正在使用 content-type:chunked 以块的形式传输数据,由 .但是,当客户端接收到数据时,它似乎是一次接收所有数据,而不是按预期的块接收。我不确定为什么会这样,希望得到任何指导。

以下是有关我的设置的一些其他详细信息:

服务器使用格式正确的块标头正确发送数据。 我已经检查了我的客户端的实现,以确保它与分块数据传输兼容。 我的网络连接没有明显的问题,但可能有防火墙或代理可能会干扰分块传输编码。

对于可能导致此问题的原因或解决方法的任何想法,我们将不胜感激。提前谢谢你!

客户端读取分块数据的代码为

fetch('https://xxxxxxxxxxxxx')
    .then((response) => {
      const stream = response.body.getReader();

      return new ReadableStream({
        async pull(controller) {
          try {
            const { done, value } = await stream.read();
            if (done) {
              controller.close();
            } else {
              controller.enqueue(value);
            }
          } catch (error) {
            controller.error(error);
          }
        },
      });
    })
    .then(async (stream) => {
      const reader = stream.getReader();
      while (true) {
        const { done, value } = await reader.read();
        if (done) {
          break;
        }
      }
    })
    .catch((error) => console.error(error));

服务器返回的数据示例为

{"x":1}\r\n{"x":1}\r\n{"x":1}\r\n{"x":1}\r\n
node.js koa chunked
2个回答
1
投票

您不会仅仅通过将

\r\n
插入响应负载来生成块。要使服务器使用分块编码,请使用
res.write
。但如果块来得太快,消费客户端可能仍会将其中的几个组合成一个
stream.read()
响应。

以下服务器端代码使您的客户端接收三个 9 字节的块。

http.createServer(function(req, res) {
  res.write(`{"x":1}\r\n`);
  setTimeout(function() {
    res.write(`{"x":1}\r\n`);
  }, 100);
  setTimeout(function() {
    res.end(`{"x":1}\r\n`);
  }, 200);
});

但是将超时减少到 0 会在客户端提供一个 27 字节的块,即使服务器仍然使用分块编码。

总而言之,您的客户端不应该依赖块边界,例如,不应该假设每个块本身都是格式良好的 JSON。

也许您真正想要的是用换行符分隔响应(

\r\n
)。下面的客户端代码使用两个
for
循环来完成此操作:外部将一个块添加到缓冲区的末尾,内部使用缓冲区开头的行。因此,块的分离与线的分离无关。

fetch(...).then(async function(response) {
  var stream = response.body.getReader();
  var buffer = "";
  for (; ;) {
    var { value, done } = await stream.read();
    if (done) break;
    buffer += String.fromCharCode(...value);
    for (; ;) {
      var offset = buffer.indexOf("\r\n");
      if (offset >= 0) {
        console.log(buffer.substring(0, offset));
        buffer = buffer.slice(offset + 2);
      } else
        break;
    }
  }
});

0
投票

Content-Type: chunked
不是东西。您的意思可能是
Transfer-Encoding: chunked
,但这需要前面一行以十六进制表示的块长度。
该编码用于在事先不知道大小的情况下发送大量数据并让客户端以块的形式获取数据。

你可以让它工作,但有更好的方法来做你想做的事。 您可以使用服务器发送的事件,它在浏览器中具有本机支持。

服务员:

app.get("/sse", async (req, res) => {
    res.writeHead(200, {
        "Content-Type": "text/event-stream",
        "Connection": "keep-alive",
        "Cache-Control": "no-cache"
    });
    for (let i=0; i<10; i++) {
        let data = {
            i,
            time: (new Date()).toString(),
        };
        res.write("data: "+JSON.stringify(data)+"\n\n");
        await sleep(1000);
    }
    res.end();
});

客户:

const events = new EventSource("/sse");
events.onmessage = event => {
    const data = JSON.parse(event.data);
    console.log(data);
};
© www.soinside.com 2019 - 2024. All rights reserved.