Backend Development 8 min read

Master Streaming Responses in Spring Boot 3: Real‑World Cases & Code

This article introduces Spring Boot 3’s StreamingResponseBody, explains its benefits for high‑concurrency scenarios, and provides three hands‑on examples—large file download, real‑time data streaming, and dynamic CSV export—complete with full Java code and practical tips for efficient backend development.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Master Streaming Responses in Spring Boot 3: Real‑World Cases & Code

1. Introduction

In high‑concurrency, large‑data scenarios, traditional synchronous responses cause memory overflow and high latency. Spring MVC’s StreamingResponseBody enables efficient asynchronous streaming, allowing “generate‑while‑send” processing for massive files, real‑time logs, dynamic CSV, or video streams with minimal resource usage.

The article covers five typical scenarios, focusing on three concrete examples.

2. Practical Cases

2.1 Large File Download

Stream a large file directly to the client to avoid memory exhaustion.

<code>@RestController
public class FileDownloadController {
    @Value("file:///d:/software/OllamaSetup.exe")
    private Resource file;
    @GetMapping("/download")
    public ResponseEntity<StreamingResponseBody> downloadFile() throws Exception {
        String fileName = file.getFilename();
        StreamingResponseBody responseBody = outputStream -> {
            try (InputStream inputStream = file.getInputStream()) {
                byte[] buffer = new byte[4096];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                    // Ensure timely sending
                    outputStream.flush();
                }
            } catch (IOException ex) {
                throw new RuntimeException("File download failed", ex);
            }
        };
        return ResponseEntity.ok()
                .header("Content-Type", "application/octet-stream")
                .header("Content-Disposition",
                        String.format("attachment; filename=%s", URLEncoder.encode(fileName, "UTF-8")))
                .body(responseBody);
    }
}
</code>

Configuration may require extending the async request timeout:

<code>spring:
  mvc:
    async:
      request-timeout: -1
</code>

Summary: Streaming download keeps memory usage constant, eliminating overflow and latency.

2.2 Real‑Time Data Stream

Push dynamically generated data such as logs or stock prices.

<code>@RestController
public class RealTimeDataController {
    @GetMapping("/stream/logs")
    public ResponseEntity<StreamingResponseBody> streamLogs() {
        StreamingResponseBody responseBody = outputStream -> {
            for (int i = 0; i < 20; i++) {
                String log = "日志数据 " + i + " - " + LocalDateTime.now() + "\n";
                outputStream.write(log.getBytes(StandardCharsets.UTF_8));
                outputStream.flush(); // crucial for real‑time output
                try {
                    Thread.sleep(new Random().nextInt(1000));
                } catch (InterruptedException e) {}
            }
        };
        return ResponseEntity.ok()
                .header("Content-Type", "text/plain;charset=utf-8")
                .body(responseBody);
    }
}
</code>

Testing with curl shows incremental output. The key is calling flush() to force data delivery.

Summary: Flushing the buffer ensures data reaches the client in real time, suitable for log monitoring.

2.3 Dynamic CSV Export

Generate and download a large CSV file without holding the entire content in memory.

<code>@RestController
public class CsvDownload {
    private final UserService userService;
    public CsvDownload(UserService userService) {
        this.userService = userService;
    }
    @GetMapping("/export/users")
    public ResponseEntity<StreamingResponseBody> exportCsv() {
        StreamingResponseBody responseBody = os -> {
            os.write("ID,Name,Email\n".getBytes());
            for (int page = 0; page < 10; page++) {
                List<User> users = userService.getUsers(page, 10);
                for (User user : users) {
                    os.write((user.id() + "," + user.name() + "," + user.email() + "\n")
                            .getBytes(StandardCharsets.UTF_8));
                }
                os.flush(); // immediate write after each page
            }
        };
        return ResponseEntity.ok()
                .header("Content-Type", "text/csv")
                .header("Content-Disposition", "attachment; filename=\"users.csv\"")
                .body(responseBody);
    }
    public static record User(Long id, String name, String email) {}
}
</code>

Summary: Paging queries and streaming writes keep memory low while exporting massive data.

Conclusion

StreamingResponseBody provides a versatile solution for high‑throughput scenarios in Spring Boot 3, enabling constant‑memory file downloads, real‑time data pushes, and efficient CSV generation.

curl test result
curl test result
Real-time StreamingBackend DevelopmentSpring Bootfile downloadCSV ExportStreamingResponseBody
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.