后端
后端通过事件流逐步发送数据 (SSE),需要设置响应头的Content-Type为'text/event-stream'
SSE是一种服务器推送技术,适合单向流式传输
node示例代码
js
const express = require('express');
const app = express();
const port = 3000;
app.get('/stream', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const text = "嗯,我现在要了解一下合规法规方面的知识。";
let index = 0;
const interval = setInterval(() => {
if (index < text.length) {
res.write(`data: ${text.charAt(index)}\n\n`); // 逐步发送字符
index++;
} else {
clearInterval(interval);
res.end(); // 结束流
}
}, 100); // 每 100ms 发送一个字符
});
app.listen(port, () => {
console.log(`后端服务运行在 http://localhost:${port}`);
});
前端
1.可以通过EventSource接收流式数据,通过onmessage事件监听数据,并逐字显示
注意:这种方式只能处理get请求,且无法自定义请求头或请求体
示例:
js
<body>
<div id="answer"></div>
<script>
const answerElement = document.getElementById('answer');
// 创建 SSE 连接
const eventSource = new EventSource('http://localhost:3000/stream');
// 监听数据事件
eventSource.onmessage = (event) => {
answerElement.textContent += event.data; // 逐步显示数据
};
// 监听流结束事件
eventSource.onerror = () => {
eventSource.close(); // 关闭连接
console.log('流已结束');
};
</script>
</body>
2.使用fetch + ReadableStream
注意:如果是post请求,需要使用fetch。当然XHR也可以通过 progress 事件实现对后端返回的流式数据的解析,但需要注意数据完整性和性能问题。如果需要更强大的流式数据处理能力,推荐使用 fetch + ReadableStream
-
ReadableStream:
-
通过 response.body.getReader() 获取 ReadableStream 的读取器。
-
使用 reader.read() 逐块读取数据。
-
-
解码数据:
- 使用 TextDecoder 将二进制数据解码为字符串。
示例:
js
<body>
<div id="output"></div>
<div id="loading">Loading...</div>
<script>
const outputElement = document.getElementById('output');
const loadingElement = document.getElementById('loading');
async function fetchStreamingData() {
const url = 'http://localhost:3000/postStream'; // 后端接口地址
const token = 'your-auth-token'; // 鉴权 token
const data = { key1: 'value1', key2: 'value2' }; // 请求参数
loadingElement.style.display = 'block'; // 显示加载动画
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const reader = response.body.getReader();
const decoder = new TextDecoder('utf-8');
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value, { stream: true });
for (const char of chunk) {
outputElement.textContent += char;
outputElement.scrollTop = outputElement.scrollHeight; // 自动滚动
}
}
} catch (error) {
console.error('Error:', error);
outputElement.textContent = 'Failed to load streaming data.';
} finally {
loadingElement.style.display = 'none'; // 隐藏加载动画
}
}
fetchStreamingData();
</script>
</body>
好啦😊已基本实现啦!当前实际开发中肯定还有一些细节需要注意,比如服务端返回的流式数据的格式,另外大多数内容都是markdown语法,如何把文本的语法格式渲染在html页面上,这些都是需要注意的哟