在 Java 中调用支持 SSE(Server-Sent Events)流式接口,并将结果流式返回给前端实现打字输出效果,你需要:
- 后端调用第三方 SSE 接口(如 ChatGPT、DeepSeek 等)。
- 后端保持连接,逐条读取数据并转发给前端。
- 前端通过 EventSource 接收流,实现类似打字的输出。
✅ 实现目标:
Java 后端接收流式响应 → SSE 推送给前端 → 前端实时打印内容
💡技术栈建议:
- Java 后端:Spring Boot + WebFlux(或原生 Servlet)
- 第三方流式接口:支持 SSE(如 OpenAI、DeepSeek 等)
- 前端:EventSource + Vue/React 原生 JavaScript 实现打字效果
🧩 一、Java 后端代码(Spring Boot 实现)
1. Maven 依赖
<!-- WebFlux 支持流式响应 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
2. 后端 Controller 示例
@RestController
@RequestMapping("/stream")
public class SseStreamController {
@GetMapping(value = "/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamFromLLM() {
WebClient client = WebClient.create("https://api.deepseek.com");
return client.post()
.uri("/v1/chat/completions")
.header("Authorization", "Bearer YOUR_API_KEY")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(Map.of(
"model", "deepseek-chat",
"stream", true,
"messages", List.of(
Map.of("role", "user", "content", "Hello!")
)
))
.retrieve()
.bodyToFlux(String.class)
.flatMap(data -> {
// 处理数据,转成 SSE 格式
String[] lines = data.split("\n");
return Flux.fromArray(lines);
})
.filter(line -> !line.isBlank() && line.startsWith("data:"))
.map(line -> line.replaceFirst("data:", "").trim())
.takeUntil(line -> line.contains("[DONE]"));
}
}
☑️
produces = MediaType.TEXT_EVENT_STREAM_VALUE
是 SSE 的关键。
🧾 示例响应格式(OpenAI / DeepSeek 类型):
data: {"choices":[{"delta":{"content":"你"},"index":0}]}
data: {"choices":[{"delta":{"content":"好"},"index":0}]}
data: [DONE]
你可以用 .map()
提取 delta.content
返回给前端。
🎨 二、前端代码(原生 JavaScript)
<div id="output"></div>
<script>
const output = document.getElementById('output');
const eventSource = new EventSource("/stream/sse");
eventSource.onmessage = function(event) {
if (event.data === "[DONE]") {
eventSource.close();
} else {
output.innerHTML += event.data; // 模拟打字效果
}
};
eventSource.onerror = function(err) {
console.error("EventSource failed:", err);
eventSource.close();
};
</script>
⛏️ 可选:模拟“打字机”动画效果(增强体验)
eventSource.onmessage = function(event) {
const text = event.data;
let i = 0;
const interval = setInterval(() => {
if (i < text.length) {
output.innerHTML += text.charAt(i++);
} else {
clearInterval(interval);
}
}, 50); // 每 50ms 打一个字
};
🧠 小提示
问题 | 解法 |
---|---|
第三方 SSE 接口断开 | 使用 retryWhen 或 doOnError 自动重试 |
中文乱码 | 设置 UTF-8 编码 produces = "text/event-stream;charset=UTF-8" |
前端不显示 | 确保 Content-Type 为 text/event-stream ,且服务端不断流 |
🧪 测试建议
- 使用 curl 测试服务端 SSE 是否成功:
curl http://localhost:8080/stream/sse
- 前端可直接使用浏览器打开页面查看实时输出。
✅ 结论总结
模块 | 实现方式 |
---|---|
Java 调用流式接口 | WebClient + Flux |
返回 SSE 数据 | @GetMapping produces="text/event-stream" |
前端接收 | EventSource + 打字效果渲染 |
支持多轮对话 | 后端保存对话上下文传入 API |
是否需要我为这个 Demo 项目打包一个完整的 Spring Boot + Vue 项目模板?或者封装一个可复用的 SSEService
?我可以继续帮你生成。
发表回复