主题
解决前端流式输出被浏览器截断的问题
更新: 9/11/2025字数: 0 字 时长: 0 分钟
一、问题背景
在开发 ai 对话项目中,使用 SSE 流式传输技术。会遇到流式数据在传输过程中被浏览器不规则地截断的问题,导致数据解析(JSON.parse())失败错误或内容不完整。
二、问题分析
当使用 Fetch API 处理流式响应时,数据是以分块(chunk)形式到达的。后端返回的数据是完整的,network中是正常的,但是浏览器在解析数据时,可能会因为某些原因(如网络问题、浏览器限制)导致数据被截断。
控制台打印日志发现完整的字符串对象只打印出来一半,导致解析失败,程序报错终止。
三、解决方案:Buffer 缓存机制
最有效的解决方案是实现一个前端 Buffer 缓存系统,将到达的数据块先缓存起来,然后按照消息边界进行完整提取和处理。
提示
创建缓冲区来累积数据块,然后根据特定分隔符(如换行符\n)从缓冲区中提取完整消息,保留下不完整的部分等待后续数据。
3.1 前端主要代码实现
javascript
try {
const response = await chatCompletions(chatRequest);
if (!response.ok) {
throw new Error(`会话接口异常`);
}
const reader = response.body?.getReader();
if (!reader) {
throw new Error(`无法获取流式数据`);
}
const decoder = new TextDecoder("utf-8");
let buffer = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
// 解码数据并添加到缓冲区
buffer += decoder.decode(value, { stream: true });
// 处理缓冲区中的完整数据行
let lineEndIndex;
while ((lineEndIndex = buffer.indexOf("\n")) !== -1) {
const line = buffer.slice(0, lineEndIndex).trim();
buffer = buffer.slice(lineEndIndex + 1);
if (line.startsWith("data: ")) {
const dataStr = line.slice(6); // 移除 "data: " 前缀
if (dataStr === "[DONE]") {
setChatHistory((prevHistory) => {
prevHistory[aiMessageIndex].loading = false; // 流结束 loading 设置为false
return prevHistory;
});
setIsStreaming(false);
return fullContent;
}
try {
const data = JSON.parse(dataStr);
// 处理数据块
if (data.delta?.content) {
fullContent += data.delta.content;
setChatHistory((prevHistory) => {
prevHistory[aiMessageIndex].content = fullContent;
return prevHistory;
});
}
if (data.finish_reason === "stop") {
console.log("完成原因:", data);
// 储存人才数据的id
if (data.message_id) {
talentDataMessageId.current = data.message_id;
}
}
} catch (e) {
alert("解析JSON失败" + e);
console.error("解析JSON失败:", e, "原始数据:", dataStr);
return false;
}
}
}
}
}
```