Java 中“流式输出”可以理解为 边处理边发送数据,而不是一次性构建完整内容再发送,这在以下场景中非常常见:

  • Web 开发中:服务端向前端发送 Server-Sent Events(SSE)
  • 命令行中:处理大型数据文件,逐行输出
  • 与 OpenAI API、搜索引擎等增量响应接口对接

下面我们从几个角度介绍 Java 实现流式输出的方式:


✅ 一、控制台中的流式输出

示例:边读取边输出

import java.io.*;

public class StreamOutputExample {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new FileReader("large.txt"));
        String line;
        while ((line = reader.readLine()) != null) {
            System.out.println(line);
            // 模拟处理延迟
            try { Thread.sleep(100); } catch (InterruptedException e) {}
        }
        reader.close();
    }
}

这种方式适用于处理大文本、日志、传感器流等。


✅ 二、Java Web 中的流式输出(Servlet)

示例:原始 Servlet 中通过 HttpServletResponse 实现流式输出:

@WebServlet("/stream")
public class StreamServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        resp.setContentType("text/plain;charset=UTF-8");
        PrintWriter out = resp.getWriter();

        for (int i = 1; i <= 10; i++) {
            out.println("第 " + i + " 行");
            out.flush();  // 🔴关键:刷新缓冲区
            try { Thread.sleep(500); } catch (InterruptedException e) {}
        }

        out.close();
    }
}

访问 /stream 会看到逐行输出的内容。


✅ 三、Spring Boot 中实现流式输出(推荐方式)

示例:返回一个 ResponseBody 的 StreamingResponseBody

@RestController
public class StreamController {

    @GetMapping("/stream")
    public ResponseEntity<StreamingResponseBody> streamText() {
        StreamingResponseBody stream = outputStream -> {
            PrintWriter writer = new PrintWriter(outputStream);
            for (int i = 1; i <= 10; i++) {
                writer.println("数据 " + i);
                writer.flush();
                Thread.sleep(500); // 模拟延迟
            }
        };

        return ResponseEntity
                .ok()
                .contentType(MediaType.TEXT_PLAIN)
                .body(stream);
    }
}

访问 /stream 会看到类似“直播”输出的效果。


✅ 四、使用 Server-Sent Events(SSE) 实现前后端实时通信

Spring Boot 中流式响应 SSE:

@RestController
public class SseController {

    @GetMapping(value = "/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter stream() {
        SseEmitter emitter = new SseEmitter();
        new Thread(() -> {
            try {
                for (int i = 1; i <= 10; i++) {
                    emitter.send(SseEmitter.event()
                            .data("第 " + i + " 条消息")
                            .id(String.valueOf(i))
                            .name("message"));
                    Thread.sleep(1000);
                }
                emitter.complete();
            } catch (Exception e) {
                emitter.completeWithError(e);
            }
        }).start();
        return emitter;
    }
}

前端配合:

<script>
  const sse = new EventSource('/sse');
  sse.onmessage = function(event) {
    console.log("收到消息:", event.data);
  }
</script>

✅ 五、配合 OpenAI 接口返回流式内容(chat/completions)

你可以使用 HttpClient 或 WebClient 发送带 "stream": true 的请求,然后逐行解析 OpenAI 返回的 SSE 数据。


🔚 总结

场景实现方式
控制台/命令行System.out + 手动 flush
ServletHttpServletResponse.getWriter() + flush()
Spring Boot RESTStreamingResponseBody
SSE 实时通信SseEmitter
与 OpenAI SSE 接口交互WebClient + 流式解析

好的,那我们就继续 Java + Spring Boot 中使用 SSE(Server-Sent Events)流式调用 OpenAI 接口的完整实现过程,包括:
✅ 前提条件、
✅ OpenAI 接口设置 stream=true
✅ 后端 Java 用 WebClient 或 OkHttp 实现 SSE 解析,
✅ 使用 SseEmitter 将数据一条条推送给前端页面。

✅ 使用场景
前端想“边生成边显示”OpenAI 返回的回答(比如 ChatGPT 效果);
后端要中转 OpenAI 的 SSE 响应,然后以 SSE 的形式再发给前端。

🧱 1. Maven 依赖
<dependencies> <!-- Spring Boot Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- WebClient 用于调用 OpenAI 接口 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> </dependencies>

📡 2. Spring Boot Controller 实现 SSE 推送
@RestController @RequestMapping("/chat") public class ChatController { private final WebClient webClient; public ChatController(WebClient.Builder builder) { this.webClient = builder.baseUrl("https://api.openai.com").build(); } @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public SseEmitter streamChat(@RequestParam String prompt) { SseEmitter emitter = new SseEmitter(0L); // 不限制时间 // 构造请求体 Map<String, Object> request = new HashMap<>(); request.put("model", "gpt-3.5-turbo"); request.put("stream", true); request.put("messages", List.of(Map.of("role", "user", "content", prompt))); webClient.post() .uri("/v1/chat/completions") .header(HttpHeaders.AUTHORIZATION, "Bearer YOUR_OPENAI_API_KEY") .contentType(MediaType.APPLICATION_JSON) .bodyValue(request) .retrieve() .bodyToFlux(String.class) .subscribe( line -> { try { // OpenAI 使用的是 text/event-stream 格式,处理 "data: ..." if (line.startsWith("data: ")) { String json = line.substring(6).trim(); if ("[DONE]".equals(json)) { emitter.complete(); return; } // 解析 delta 内容 ObjectMapper mapper = new ObjectMapper(); JsonNode root = mapper.readTree(json); String content = root .path("choices").get(0) .path("delta").path("content").asText(); if (!content.isEmpty()) { emitter.send(SseEmitter.event().data(content)); } } } catch (Exception e) { emitter.completeWithError(e); } }, error -> emitter.completeWithError(error), emitter::complete ); return emitter; } }

🖥️ 3. 前端页面接收 SSE(简单 HTML 示例)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>SSE Chat</title> </head> <body> <h2>AI回答:</h2> <div id="response"></div> <script> const source = new EventSource("/chat/stream?prompt=你好,介绍一下SSE技术"); source.onmessage = function(event) { document.getElementById("response").innerHTML += event.data; }; source.onerror = function(err) { console.error("SSE 错误", err); source.close(); }; </script> </body> </html>

🎯 效果
打开页面后,OpenAI 会逐段返回内容,Java 后端边接收边推送给前端,用户会看到一段一段地生成文本,仿佛 ChatGPT 一样。

✅ 你可以自定义的部分
选择 GPT-4、GPT-3.5 模型;
修改 SseEmitter 推送内容格式,支持 Markdown、HTML;
添加 Redis 缓存、上下文管理等;
前端可用 Vue/React 组件来包裹。