流式响应Node.js + Express和fetch示例

1,428 阅读3分钟

流式响应(Stream Response)解析及示例

流式响应(Stream Response)是一种在网络通信中特别常见的技术模式,特别是在需要处理大文件传输、长连接任务、或服务器推送更新时显得非常有用。相比传统的完整响应,流式响应可以逐步发送数据,提高响应速度和用户体验。本文将对流式响应进行详细解析,并提供简单示例代码。

什么是流式响应?

流式响应是一种将数据分片逐步发送到客户端的技术,也就是说,服务器可以在数据仍在生成或检索过程中即开始发送部分数据。这样客户应用可以更早开始处理接收到的数据,而不必等到整个数据集准备好后再开始处理。

优势

  1. 减少延迟:数据可以在生成时立即被发送,不需等到整个数据准备完成。
  2. 内存优化:仅用到的部分数据才需保存在内存中,适用于大数据集。
  3. 更好的用户体验:用户可以获得即时反馈,特别是当响应数据量大时。

典型应用场景

  1. 大文件下载:如视频流、文件下载等。
  2. 实时数据推送:如聊天应用、实时通知。
  3. 代理和缓存服务:如反向代理服务器。

示例代码

为了更好地展示使用Node.js实现流式响应,我们将采用Express框架,这是一个轻量级且受欢迎的Node.js框架。

使用fetch来获取流式数据需要一些不同的处理方法,因为fetch默认不会逐步解析响应体。不过,我们可以通过读取ReadableStream来实现逐步处理流式数据。

下面是一个Node.js + Express的服务端示例,以及一个使用fetch获取流式数据的客户端示例。

服务端代码(Node.js + Express)

首先,创建一个名为 server.js 的文件,内容如下:

const express = require('express');
const app = express();
const port = 3000;

function generateData(res) {
    let count = 0;
    const intervalId = setInterval(() => {
        if (count > 10) {
            clearInterval(intervalId);
            res.end();  // 结束HTTP响应
            return;
        }
        res.write(`data: ${count}\n\n`); // 传输数据
        count++;
    }, 1000);  // 每秒发送一次数据
}

app.get('/stream', (req, res) => {
    res.setHeader('Content-Type', 'text/plain');
    res.setHeader('Cache-Control', 'no-cache');
    res.setHeader('Connection', 'keep-alive');
    
    generateData(res);
});

app.listen(port, () => {
  console.log(`Server is running at http://localhost:${port}`);
});

这个示例中,generateData 函数每秒生成一次数据并通过 res.write 发送给客户端。在这里,Content-Type 设置为 text/plain 以便演示。

客户端代码(JavaScript)

客户端代码稍微复杂一些,因为fetch默认不会逐步解析流式响应。我们需要使用ReadableStream来处理逐步读取的数据。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Stream Response Example with Fetch</title>
</head>
<body>
    <h1>Stream Response Example with Fetch</h1>
    <div id="data"></div>
    <script>
        async function fetchStream() {
            const response = await fetch("http://localhost:3000/stream");
            
            if (!response.body) {
                throw new Error('ReadableStream not supported in this browser.');
            }

            const reader = response.body.getReader();
            const decoder = new TextDecoder('utf-8');

            function appendData(text) {
                const newElement = document.createElement("div");
                newElement.textContent = text;
                document.getElementById("data").appendChild(newElement);
            }

            while (true) {
                const { done, value } = await reader.read();

                if (done) {
                    break;
                }

                const text = decoder.decode(value, { stream: true });
                appendData(text.trim()); // 去除多余的空白字符
            }
        }

        fetchStream().catch(error => console.error("Error fetching stream:", error));
    </script>
</body>
</html>

运行程序

  1. 启动Node.js服务端:
node server.js
  1. 打开包含客户端HTML和JavaScript代码的HTML文件,在浏览器中访问。

你将会看到客户端逐步接收到来自服务端的流式数据,并显示在页面上。

总结

通过使用fetchReadableStream,我们可以在客户端逐步处理流式响应数据。这种方法非常适合在现代浏览器中处理需要逐步获取和处理的大数据集或实时数据流。

Node.js和Express的流式响应特性配合fetch的逐步解析,使得这种技术更易于在现代Web应用中实现和应用。希望这个示例能帮助你理解如何用fetch实现流式数据获取!