Here's an example of a Spring Boot Controller that accepts a request payload of type EmployeeRequest
, generates a CSV file based on the input data, compresses the file into a ZIP, and returns the zipped file as a response:
Spring Boot Controller
java
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@RestController
@RequestMapping("/api/employee")
public class EmployeeController {
@PostMapping("/generate")
public ResponseEntity<StreamingResponseBody> generateZippedCsv(@RequestBody EmployeeRequest request) {
StreamingResponseBody responseBody = outputStream -> {
// Create CSV in memory
ByteArrayOutputStream csvOutputStream = new ByteArrayOutputStream();
try (CSVPrinter csvPrinter = new CSVPrinter(new PrintWriter(csvOutputStream), CSVFormat.DEFAULT)) {
// Add CSV Headers
csvPrinter.printRecord("First Name", "Last Name", "Middle Name", "Date of Birth");
// Populate CSV rows with request data
for (Employee employee : request.getEmployees()) {
csvPrinter.printRecord(
employee.getFirstName(),
employee.getLastName(),
employee.getMiddleName(),
employee.getDateOfBirth()
);
}
}
// Create ZIP containing the CSV
try (ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream)) {
zipOutputStream.putNextEntry(new ZipEntry("employees.csv"));
zipOutputStream.write(csvOutputStream.toByteArray());
zipOutputStream.closeEntry();
}
};
// Set headers for ZIP file download
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.set(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"employees.zip\"");
return ResponseEntity.ok()
.headers(headers)
.body(responseBody);
}
}
Supporting Classes
EmployeeRequest
This class represents the input request payload that contains a list of employees.
java
import java.util.List;
public class EmployeeRequest {
private List<Employee> employees;
// Getters and Setters
public List<Employee> getEmployees() {
return employees;
}
public void setEmployees(List<Employee> employees) {
this.employees = employees;
}
}
Employee
This class represents the individual employee data.
java
public class Employee {
private String firstName;
private String lastName;
private String middleName;
private String dateOfBirth; // Use String for simplicity; can use LocalDate or Date instead
// Getters and Setters
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getMiddleName() {
return middleName;
}
public void setMiddleName(String middleName) {
this.middleName = middleName;
}
public String getDateOfBirth() {
return dateOfBirth;
}
public void setDateOfBirth(String dateOfBirth) {
this.dateOfBirth = dateOfBirth;
}
}
How It Works
- Input: The API receives a JSON payload containing a list of employees with their details (
firstName
, lastName
, middleName
, and dateOfBirth
). - CSV Generation: It generates a CSV file with these details in memory.
- ZIP Compression: The CSV file is compressed into a ZIP archive.
- Response: The ZIP file is returned to the client with appropriate headers for file download.
firstName
, lastName
, middleName
, and dateOfBirth
).Example Request
POST Request
httpPOST /api/employee/generate Content-Type: application/json
Request Body
json
{
"employees": [
{
"firstName": "John",
"lastName": "Doe",
"middleName": "A.",
"dateOfBirth": "1985-03-15"
},
{
"firstName": "Jane",
"lastName": "Smith",
"middleName": "B.",
"dateOfBirth": "1990-07-21"
}
]
}
Response
The API returns a employees.zip
file containing the CSV file. The CSV will look like this:
csvFirst Name,Last Name,Middle Name,Date of Birth John,Doe,A.,1985-03-15 Jane,Smith,B.,1990-07-21
1. Spring Boot Configuration Limits
Spring Boot uses Servlet multipart handling for file uploads and responses. The maximum file size is governed by the
spring.servlet.multipart
properties:
spring.servlet.multipart.max-file-size
:
- Limits the maximum size of a single uploaded file or generated response.
- Default:
1MB
.
spring.servlet.multipart.max-request-size
:
- Limits the total size of all files in a multipart request or response.
- Default:
10MB
.Example Configuration in
application.properties
:propertiesspring.servlet.multipart.max-file-size=100MB spring.servlet.multipart.max-request-size=100MB
2. Server-Specific Limits
Your server configuration (e.g., Tomcat, Jetty) also enforces limits:
Tomcat:
server.tomcat.max-swallow-size
: Specifies the maximum size of a request body or response.
- Default:
2MB
.- Set
-1
for no limit.Example:
propertiesserver.tomcat.max-swallow-size=-1
Other Servers:
- Jetty, Undertow, or other embedded servers might have similar settings for request/response size limits. Check their documentation for details.
3. JVM Memory Limits
Your application must have enough memory to handle large files, especially for:
- In-Memory Operations: When files are stored or manipulated in memory (e.g., CSV generation or zipping).
- Heap Space: Ensure your JVM has adequate heap memory to avoid
OutOfMemoryError
.JVM Options:
bash-Xms512M -Xmx2G
This example sets the JVM to use 512MB initial memory and 2GB maximum memory.
4. Operating System Limits
The underlying operating system may impose limits on:
- Temporary File Storage: For file uploads or in-memory buffers, check
/tmp
(or equivalent) storage space.- Maximum Open File Handles: Ensure you are not exceeding the file descriptor limits, particularly in high-concurrency scenarios.
5. Practical Limits for Download Responses
For downloading files (like a CSV zipped file):
- StreamingResponseBody: Use it to stream large files directly to the response output stream, avoiding in-memory buffering.
- This approach minimizes memory usage, allowing responses to exceed typical memory constraints.
6. Network Bandwidth
Large file downloads are also affected by:
- Network bandwidth between the server and the client.
- Timeouts due to prolonged transfers.
Ensure:
- Timeouts are configured appropriately (
server.connection-timeout
in Spring Boot).- Clients can handle large file downloads.
Summary
- Default Spring Boot limits are 1MB per file and 10MB per request.
- You can increase these limits using properties like
spring.servlet.multipart.max-file-size
.- Use
StreamingResponseBody
for large file downloads to avoid memory bottlenecks.- Monitor JVM memory, server configurations, and OS limits to ensure performance and reliability.
If you are working with extremely large files (e.g., several GB), consider a more robust solution like:
- Direct S3 File Downloads: Upload the file to AWS S3 and share a pre-signed URL.
- Chunked File Downloads: Break the file into smaller chunks for transmission.