在浏览器中从 express 获取响应块
Get a response in chunks from express in browser
我正在为一些长时间 运行 服务器端任务(最多几分钟)构建一个进度条,我想要一种显示任务进度的方法。我可以使用 WebSockets 或按时间间隔轮询,但我不想跟踪每个任务。相反,我想使用长轮询并将进度更新写入流。
这是路由在服务器上应该是什么样子的演示
app.get('/test', (req, res) => {
let num = 0;
const interval = setInterval(() => res.write(num++ + ' '), 300);
setTimeout(() => {
clearInterval(interval);
res.send();
}, 5000);
});
使用 -N
在该端点上执行 cURL 工作得很好,但是,在浏览器中实现它时我遇到了一些问题。
我试过像这样获取:
const response = await fetch(url);
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done)
break;
console.log(decoder.decode(value));
}
这在 Chrome 上工作得很好,但在 firefox 上不行,正如您所看到的,这里不支持它:
https://caniuse.com/mdn-api_windoworworkerglobalscope_fetch_streaming_response_body
不过,我尝试了一种不同的方法,这次是使用 XHR
const xhr = new XMLHttpRequest()
xhr.open("GET", url)
xhr.onprogress = function () {
console.log(xhr.responseText);
};
xhr.send();
这在 Firefox 中完美运行,但在 Chrome 中,onProgress 事件仅在处理完整个请求后触发。我也试过 onReadyStateChange
,但结果是同样的问题。
>_< 当它在任一浏览器中更新时,如何分块读取该死的数据?我想我可以试试 Axios,但我真的需要这个吗?
编辑:可能值得一提的是 Chrome 和 Firefox 似乎以不同方式处理获取行为。使用 Chrome,我可以在抓取完成之前使用抓取对象,所以我
const response = await fetch(url);
console.log("Preflight complete, fetch is not done though");
但在 Firefox 中,控制台日志只有在提取完成后才会执行。这就是为什么我认为我不能在 Firefox 中使用响应主体,但我可以使用 Chrome.
根据这个 GitHub 问题:
https://github.com/ratpack/ratpack/issues/443#issuecomment-59621215
这是一个 Chrome/Webkit 错误。将请求的 Content-Type
更改为 text/plain
以外的任何内容,使其与 Chrome 上的 XHR
一起工作。因此,如果我将服务器响应更改为
app.get('/test', (req, res) => {
let num = 0;
let interval = setInterval(() => res.write(num++ + ' '), 300);
// THIS IS A DUMB HACK TO GET CHROME TO PLAY NICE X_X
res.setHeader('Content-Type', 'text/html');
setTimeout(() => {
clearInterval(interval);
res.send();
}, 5000);
});
令人震惊的是,这似乎也解决了在 Firefox 中使用未修改的标志获取流的问题。我想我现在会使用 XHR
方法,只是因为它更兼容一点,但是,鉴于每个新块都是单独处理的,获取版本更容易使用。
啊啊啊啊啊啊啊啊
我正在为一些长时间 运行 服务器端任务(最多几分钟)构建一个进度条,我想要一种显示任务进度的方法。我可以使用 WebSockets 或按时间间隔轮询,但我不想跟踪每个任务。相反,我想使用长轮询并将进度更新写入流。
这是路由在服务器上应该是什么样子的演示
app.get('/test', (req, res) => {
let num = 0;
const interval = setInterval(() => res.write(num++ + ' '), 300);
setTimeout(() => {
clearInterval(interval);
res.send();
}, 5000);
});
使用 -N
在该端点上执行 cURL 工作得很好,但是,在浏览器中实现它时我遇到了一些问题。
我试过像这样获取:
const response = await fetch(url);
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done)
break;
console.log(decoder.decode(value));
}
这在 Chrome 上工作得很好,但在 firefox 上不行,正如您所看到的,这里不支持它:
https://caniuse.com/mdn-api_windoworworkerglobalscope_fetch_streaming_response_body
不过,我尝试了一种不同的方法,这次是使用 XHR
const xhr = new XMLHttpRequest()
xhr.open("GET", url)
xhr.onprogress = function () {
console.log(xhr.responseText);
};
xhr.send();
这在 Firefox 中完美运行,但在 Chrome 中,onProgress 事件仅在处理完整个请求后触发。我也试过 onReadyStateChange
,但结果是同样的问题。
>_< 当它在任一浏览器中更新时,如何分块读取该死的数据?我想我可以试试 Axios,但我真的需要这个吗?
编辑:可能值得一提的是 Chrome 和 Firefox 似乎以不同方式处理获取行为。使用 Chrome,我可以在抓取完成之前使用抓取对象,所以我
const response = await fetch(url);
console.log("Preflight complete, fetch is not done though");
但在 Firefox 中,控制台日志只有在提取完成后才会执行。这就是为什么我认为我不能在 Firefox 中使用响应主体,但我可以使用 Chrome.
根据这个 GitHub 问题:
https://github.com/ratpack/ratpack/issues/443#issuecomment-59621215
这是一个 Chrome/Webkit 错误。将请求的 Content-Type
更改为 text/plain
以外的任何内容,使其与 Chrome 上的 XHR
一起工作。因此,如果我将服务器响应更改为
app.get('/test', (req, res) => {
let num = 0;
let interval = setInterval(() => res.write(num++ + ' '), 300);
// THIS IS A DUMB HACK TO GET CHROME TO PLAY NICE X_X
res.setHeader('Content-Type', 'text/html');
setTimeout(() => {
clearInterval(interval);
res.send();
}, 5000);
});
令人震惊的是,这似乎也解决了在 Firefox 中使用未修改的标志获取流的问题。我想我现在会使用 XHR
方法,只是因为它更兼容一点,但是,鉴于每个新块都是单独处理的,获取版本更容易使用。