You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

StreamingResponseBody复用未关闭的OutputStream是否符合预期?请求复用引发异常

Is Reusing an Unclosed OutputStream from StreamingResponseBody Expected Behavior?

Short answer: Absolutely not—this is a bug, not intended behavior. Let's break down what's happening and how to fix it.

Why this is wrong

The StreamingResponseBody (and its associated OutputStream) is built for a single request-response cycle. That OutputStream is tightly bound to the underlying HTTP connection of the specific request you're handling. When you terminate a curl request with ctrl+c, the connection gets abruptly dropped (the "broken pipe" scenario), and the servlet container (like Tomcat or Jetty) immediately cleans up the stream's backing resources. At that point, the stream becomes completely invalid—any later attempt to write to it will throw exceptions like NullPointerException or a broken pipe IOException, exactly what you're encountering.

Why you're seeing reuse

If your system is reusing the same OutputStream instance (same hash code) across different requests, you're almost certainly accidentally caching or sharing the stream (or the StreamingResponseBody itself) in a shared scope—like a static variable, a singleton bean, or a request-scoped bean that's being incorrectly retained. This is a classic mistake that leads to cross-request contamination.

How to fix it

You need to enforce strict per-request isolation for StreamingResponseBody and its stream:

  • Never cache the OutputStream: The stream passed to StreamingResponseBody.writeTo() is only valid for the duration of that method call. Don't store it anywhere outside this method's scope.
  • Return a fresh StreamingResponseBody per request: Every incoming request should get its own instance of StreamingResponseBody, tailored to that request's specific parameters.
  • Handle exceptions gracefully: In your writeTo implementation, catch broken pipe exceptions and log them instead of letting them propagate—this avoids noisy error logs when clients terminate requests early.

Here's a clean example of how this should look:

@GetMapping("/stream")
public StreamingResponseBody streamResponse(@RequestParam String param) {
    // Create a NEW StreamingResponseBody for every request
    return outputStream -> {
        try {
            // Write data specific to this request's parameters
            byte[] content = String.format("Processing request with param: %s%n", param).getBytes();
            outputStream.write(content);
            outputStream.flush();
        } catch (IOException e) {
            // Handle client-side termination gracefully
            if (e.getMessage() != null && e.getMessage().contains("Broken pipe")) {
                log.warn("Client terminated the request early (broken pipe)");
            } else {
                // Rethrow other IO exceptions
                throw e;
            }
        }
    };
}

Double-check your code for any places where you might be storing the StreamingResponseBody or its OutputStream in a shared variable—that's almost certainly where the issue originates.

内容的提问来源于stack exchange,提问作者aglassman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 04:06:46