Introduction
Yes, using @RequestParam for MultipartFile is the standard and correct approach in Spring Boot for handling file uploads. The annotation binds a multipart form field to a MultipartFile parameter in your controller method. For more complex scenarios with additional metadata, use @RequestPart instead. Both annotations work with multipart/form-data requests and are fully supported by Spring's multipart resolver.
Basic File Upload with @RequestParam
1@RestController
2@RequestMapping("/api/files")
3public class FileUploadController {
4
5 @PostMapping("/upload")
6 public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
7 if (file.isEmpty()) {
8 return ResponseEntity.badRequest().body("File is empty");
9 }
10
11 String filename = file.getOriginalFilename();
12 long size = file.getSize();
13 String contentType = file.getContentType();
14
15 // Save to disk
16 Path destination = Path.of("/uploads", filename);
17 file.transferTo(destination);
18
19 return ResponseEntity.ok("Uploaded: " + filename + " (" + size + " bytes)");
20 }
21}
The client sends a multipart/form-data request with a form field named "file".
cURL Test
curl -X POST http://localhost:8080/api/files/upload \
-F "file=@/path/to/document.pdf"
Multiple File Upload
1@PostMapping("/upload-multiple")
2public ResponseEntity<String> uploadMultiple(
3 @RequestParam("files") List<MultipartFile> files) {
4
5 for (MultipartFile file : files) {
6 if (!file.isEmpty()) {
7 file.transferTo(Path.of("/uploads", file.getOriginalFilename()));
8 }
9 }
10
11 return ResponseEntity.ok("Uploaded " + files.size() + " files");
12}
1@PostMapping("/upload-with-metadata")
2public ResponseEntity<String> uploadWithMetadata(
3 @RequestParam("file") MultipartFile file,
4 @RequestParam("description") String description,
5 @RequestParam(value = "category", defaultValue = "general") String category) {
6
7 // file, description, and category are all extracted from the multipart form
8 return ResponseEntity.ok(
9 "File: " + file.getOriginalFilename() +
10 ", Description: " + description +
11 ", Category: " + category
12 );
13}
1curl -X POST http://localhost:8080/api/files/upload-with-metadata \
2 -F "[email protected]" \
3 -F "description=Annual report" \
4 -F "category=reports"
@RequestParam vs @RequestPart
1// @RequestParam: simple file + string fields
2@PostMapping("/simple")
3public void simple(
4 @RequestParam("file") MultipartFile file,
5 @RequestParam("name") String name) { }
6
7// @RequestPart: file + JSON body parts (with content type)
8@PostMapping("/complex")
9public void complex(
10 @RequestPart("file") MultipartFile file,
11 @RequestPart("metadata") FileMetadata metadata) { }
12// FileMetadata is deserialized from JSON automatically
@RequestPart uses the Content-Type header of each part for deserialization. Use it when sending JSON alongside files:
curl -X POST http://localhost:8080/api/files/complex \
-F "[email protected]" \
-F "metadata={\"title\":\"Report\",\"year\":2025};type=application/json"
File Size Configuration
1# application.yml
2spring:
3 servlet:
4 multipart:
5 max-file-size: 10MB # Max single file size
6 max-request-size: 50MB # Max total request size
7 enabled: true # Enable multipart (default: true)
8 file-size-threshold: 2KB # Threshold to write to disk vs memory
# application.properties
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=50MB
File Validation
1@PostMapping("/upload")
2public ResponseEntity<String> upload(@RequestParam("file") MultipartFile file) {
3 // Check file is not empty
4 if (file.isEmpty()) {
5 return ResponseEntity.badRequest().body("File is empty");
6 }
7
8 // Validate file size (programmatic check)
9 if (file.getSize() > 5 * 1024 * 1024) { // 5MB
10 return ResponseEntity.badRequest().body("File too large");
11 }
12
13 // Validate content type
14 String contentType = file.getContentType();
15 List<String> allowedTypes = List.of("image/jpeg", "image/png", "application/pdf");
16 if (!allowedTypes.contains(contentType)) {
17 return ResponseEntity.badRequest().body("Invalid file type: " + contentType);
18 }
19
20 // Validate file extension
21 String filename = file.getOriginalFilename();
22 if (filename != null && !filename.matches(".*\\.(jpg|jpeg|png|pdf)$")) {
23 return ResponseEntity.badRequest().body("Invalid file extension");
24 }
25
26 file.transferTo(Path.of("/uploads", filename));
27 return ResponseEntity.ok("Uploaded successfully");
28}
Saving to Different Locations
1// Save to disk
2file.transferTo(Path.of("/uploads", file.getOriginalFilename()));
3
4// Read as byte array (for database storage)
5byte[] bytes = file.getBytes();
6
7// Read as InputStream (for streaming to cloud storage)
8try (InputStream inputStream = file.getInputStream()) {
9 // Upload to S3, GCS, Azure Blob, etc.
10 s3Client.putObject(bucket, key, inputStream, metadata);
11}
Exception Handling
1@ControllerAdvice
2public class FileUploadExceptionHandler {
3
4 @ExceptionHandler(MaxUploadSizeExceededException.class)
5 public ResponseEntity<String> handleMaxSizeException(MaxUploadSizeExceededException ex) {
6 return ResponseEntity.status(HttpStatus.PAYLOAD_TOO_LARGE)
7 .body("File size exceeds the maximum allowed size");
8 }
9
10 @ExceptionHandler(MultipartException.class)
11 public ResponseEntity<String> handleMultipartException(MultipartException ex) {
12 return ResponseEntity.badRequest()
13 .body("Invalid multipart request: " + ex.getMessage());
14 }
15}
Common Pitfalls
Missing multipart/form-data content type: The client must send the request with Content-Type: multipart/form-data. Sending JSON with a file field does not work — the file must be a multipart form part. Most HTTP clients (Postman, cURL with -F) set this automatically.
Parameter name mismatch:
@RequestParam("file") must match the form field name exactly. If the client sends
[email protected] but the annotation says
@RequestParam("document"), Spring returns a 400 error with "Required request part 'document' is not present".
File size limit exceeded silently: The default max-file-size is 1MB. Large uploads fail with a MaxUploadSizeExceededException. Configure spring.servlet.multipart.max-file-size and max-request-size in application properties.
Using @RequestBody instead of @RequestParam: @RequestBody reads the entire request body as a single object (for JSON). For file uploads, use @RequestParam or @RequestPart. Using @RequestBody with MultipartFile causes a "Content type not supported" error.
Not sanitizing file names: file.getOriginalFilename() returns the client-provided filename, which may contain path traversal characters like ../. Always sanitize the filename before saving: Paths.get(filename).getFileName().toString().
Summary
@RequestParam("file") MultipartFile file is the standard approach for file uploads in Spring Boot
Use @RequestPart when combining files with JSON metadata in the same request
Configure max-file-size and max-request-size in application properties for large uploads
Validate file size, content type, and file extension before processing
Always sanitize getOriginalFilename() to prevent path traversal attacks
Handle MaxUploadSizeExceededException with a @ControllerAdvice for user-friendly error messages