我正在使用 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
您不会仅仅通过将
\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;
}
}
});
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);
};