Backend Development 7 min read

How to Fix Resource Leaks When Streaming Files in SpringBoot 3.2

This article explains why using a wrong Content‑Type header and not closing the InputStream when returning a Resource in SpringBoot 3.2 causes file‑handle leaks, and demonstrates three solutions—including try‑with‑resources, direct HttpServletResponse streaming, and custom Resource implementations—to safely download files even under high concurrency.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
How to Fix Resource Leaks When Streaming Files in SpringBoot 3.2

Environment: SpringBoot 3.2.5

1. Reproduction Issue

The problem occurs when using Resource to download a file. The example controller deliberately sets an incorrect Content‑Type header ("textplain" instead of "*/*") and returns a ResponseEntity<Resource> . The request fails with a Content‑Type error, and more seriously, the opened file stream is never closed, leading to a resource leak that can exhaust file handles under high concurrency.

The leak is caused by not closing the file stream.

2. Solutions

2.1 Incorrect Approach (Fixed Content‑Type Only)

Using try‑with‑resources to ensure the stream is closed:

<code>@RestController
@RequestMapping("/download")
public class StreamDownloadController {
    @GetMapping("")
    public ResponseEntity&lt;Resource&gt; download() throws Exception {
        try (InputStream is = new FileInputStream(new File("d:\\1.txt"))) {
            Resource resource = new InputStreamResource(is);
            return ResponseEntity.ok()
                    .header(HttpHeaders.CONTENT_TYPE, "text/plain")
                    .body(resource);
        }
    }
}
</code>

Note: This code works and the Content‑Type is correct.

If you keep the same return type, a Stream Closed error can appear because Spring MVC reads the stream after the controller method has finished and the try‑with‑resources block has already closed it.

Calling the download endpoint returns a ResponseEntity . When the method ends, the try‑with‑resources block closes the InputStream.

Spring MVC then uses a HandlerMethodReturnValueHandler to write the response. The handler attempts to read the stream, but it has already been closed, causing the error.

2.2 Correct Approaches

Method 1 : Write directly to HttpServletResponse and avoid ResponseEntity . <code>@GetMapping("") public void download(HttpServletResponse response) throws Exception { try (InputStream is = new FileInputStream(new File("d:\\1.txt"))) { response.setContentType("application/octet-stream"); is.transferTo(response.getOutputStream()); } } </code>

Method 2 : Keep ResponseEntity but defer opening the stream until the response is written by providing a custom Resource implementation. <code>@GetMapping("") public ResponseEntity&lt;Resource&gt; download3() throws Exception { Resource resource = new AbstractResource() { @Override public InputStream getInputStream() throws IOException { return new FileInputStream(new File("d:\\1.txt")); } @Override public String getDescription() { return "下载文件"; } }; return ResponseEntity.ok() .header(HttpHeaders.CONTENT_TYPE, "application/octet-stream") .body(resource); } </code>

Method 3 : Upgrade to Spring Framework 6.1.7 (or later) where InputStreamResource has a constructor accepting an InputStreamSource . This allows a lambda‑based lazy stream opening. <code>@GetMapping("") public ResponseEntity&lt;Resource&gt; download() throws Exception { Resource resource = new InputStreamResource( new FileSystemResource(new File("d:\\1.txt")) ); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_TYPE, "application/octet-stream") .body(resource); } </code> Configure the newer Spring version in your build: <code>&lt;spring-framework.version&gt;6.1.7&lt;/spring-framework.version&gt; </code>

All three methods ensure the file handle is released properly, preventing resource exhaustion in high‑concurrency scenarios.

— End of article.

file downloadSpringBoottry-with-resourcesresource leakResponseEntity
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.