在 Java 中调用支持 SSE(Server-Sent Events)流式接口,并将结果流式返回给前端实现打字输出效果,你需要:

  1. 后端调用第三方 SSE 接口(如 ChatGPT、DeepSeek 等)。
  2. 后端保持连接,逐条读取数据并转发给前端。
  3. 前端通过 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,且服务端不断流

🧪 测试建议

  1. 使用 curl 测试服务端 SSE 是否成功:
curl http://localhost:8080/stream/sse
  1. 前端可直接使用浏览器打开页面查看实时输出。

✅ 结论总结

模块实现方式
Java 调用流式接口WebClient + Flux
返回 SSE 数据@GetMapping produces="text/event-stream"
前端接收EventSource + 打字效果渲染
支持多轮对话后端保存对话上下文传入 API

是否需要我为这个 Demo 项目打包一个完整的 Spring Boot + Vue 项目模板?或者封装一个可复用的 SSEService?我可以继续帮你生成。