How do you receive an uploaded file in a Spring MVC controller, and what does MultipartFile give you?
answer
- @RequestParam("file") MultipartFile
- multipart/form-data enctype
- transferTo() over getBytes()
- getOriginalFilename() is untrusted
- List<MultipartFile> for many
basics
~10 sAdd a handler method parameter of type MultipartFile annotated with @RequestParam matching the form field name. Spring binds the uploaded part to it; then call getOriginalFilename(), getBytes()/getInputStream(), getSize(), and transferTo() to save it.
solid answer
~30 sFor a browser form submitted as multipart/form-data, declare a controller method that takes org.springframework.web.multipart.MultipartFile, annotated @RequestParam("file") so the annotation value matches the form field name. Spring's MultipartResolver parses the request and binds the matching part. MultipartFile exposes getOriginalFilename() (client-supplied name, untrusted), getContentType(), getSize(), isEmpty(), getBytes(), getInputStream(), and transferTo(Path) to persist it. Use getInputStream() or transferTo() rather than getBytes() to avoid loading large files fully into memory. For multiple files use List<MultipartFile> or MultipartFile[]. Return the endpoint via a POST mapping consuming multipart/form-data. Spring Boot auto-configures the resolver, so no extra XML/bean is needed.
code
java · 20 lines@RestController
public class UploadController {
private final Path uploadDir = Path.of("/var/app/uploads");
@PostMapping(path = "/files", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<String> upload(@RequestParam("file") MultipartFile file) throws IOException {
if (file.isEmpty()) {
return ResponseEntity.badRequest().body("empty file");
}
// Never trust the client filename — generate our own.
String stored = UUID.randomUUID() + "-" + StringUtils.getFilename(file.getOriginalFilename());
Path dest = uploadDir.resolve(stored).normalize();
if (!dest.startsWith(uploadDir)) { // path-traversal guard
throw new IllegalArgumentException("bad path");
}
file.transferTo(dest); // streams/moves temp file
return ResponseEntity.ok(stored);
}
}go deeper
Know the annotation + type: @RequestParam("file") MultipartFile, and the basic getters/transferTo.
Explain streaming vs getBytes, List<MultipartFile>, and why getOriginalFilename is untrusted.
Add path-traversal defense, temp-file lifecycle, and mixing text fields via @ModelAttribute.
Frame memory/back-pressure and safe storage naming as part of an upload contract and resource policy.
## The problem When a user uploads a file through an HTML form, the browser encodes the request body as **`multipart/form-data`** (set via `enctype="multipart/form-data"` on the `<form>`). This format splits the body into multiple *parts*, each with its own headers (`Content-Disposition`, `Content-Type`), separated by a boundary string. A normal `@RequestBody`/`@RequestParam` can't parse this directly — you need multipart handling. ## The pieces - **`MultipartResolver`** — a Spring interface that detects multipart requests and parses them. Spring Boot auto-registers `StandardServletMultipartResolver`, which delegates to the Servlet container's built-in multipart parsing (`HttpServletRequest.getParts()`). You normally do nothing to enable it. - **`MultipartFile`** (`org.springframework.web.multipart.MultipartFile`) — Spring's abstraction over one uploaded part. Key methods: - `getOriginalFilename()` — the filename the *client* sent. **Untrusted** — never use it directly as a filesystem path (path-traversal risk). - `getContentType()` — client-declared MIME type. Also untrusted (spoofable). - `getSize()` — bytes. - `isEmpty()` — true if no file was selected. - `getBytes()` — loads the whole file into a `byte[]` (memory-heavy; avoid for large files). - `getInputStream()` — stream the content (preferred for large files). - `transferTo(Path dest)` / `transferTo(File dest)` — efficiently move/write the (possibly already on-disk) temp file to a destination. ## Binding it Declare a handler parameter and bind by form-field name: ```java @PostMapping(path = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity<String> upload(@RequestParam("file") MultipartFile file) { ... } ``` The `@RequestParam` value (`"file"`) must match the `name` attribute of the file input. If you omit the value it defaults to the parameter name (requires `-parameters` compilation). `required = false` lets the field be optional; otherwise a missing part throws `MissingServletRequestPartException`/`MissingServletRequestParameterException`. ## Multiple files and mixed forms - `List<MultipartFile>` or `MultipartFile[]` for several files under the same field name. - Regular text fields in the same form bind with ordinary `@RequestParam String` or via a `@ModelAttribute` command object (which may itself contain a `MultipartFile` field). ## Saving safely ```java String safeName = UUID.randomUUID().toString(); Path dest = uploadDir.resolve(safeName).normalize(); file.transferTo(dest); ``` Generate your own name; don't trust `getOriginalFilename()`. `transferTo` is efficient because the container often already spooled the part to a temp file — it may just rename/move it. ## Gotchas - Forgetting `enctype="multipart/form-data"` on the form → the file arrives as a normal field and `MultipartFile` is empty/missing. - Using `getBytes()` on big files → `OutOfMemoryError`. - Temp files are cleaned up by the container when the request completes, so read/transfer within the request, not later on another thread. - Default size limits (1MB file / 10MB request in Spring Boot) can reject legitimate uploads; tune `spring.servlet.multipart.*`. ## When to use Any endpoint accepting browser/form or API file uploads. For pure-JSON APIs that also carry a file, combine `@RequestPart` for a JSON metadata part with a `MultipartFile` part.
- Why prefer transferTo() or getInputStream() over getBytes()?getBytes() materializes the entire file into a heap byte[], risking OutOfMemoryError under concurrent large uploads. transferTo() moves the already-spooled temp file (often a cheap rename), and getInputStream() lets you stream to disk/S3 with bounded memory.
- What happens if the form isn't set to enctype=multipart/form-data?The browser sends application/x-www-form-urlencoded; the file content isn't a real part, so the request isn't treated as multipart and the MultipartFile parameter is empty or the part is missing (MissingServletRequestPartException).
saying these in an interview costs you the question
- Using getOriginalFilename() directly as the save path (path traversal).
- Claiming you must manually register a MultipartResolver in Spring Boot (it's auto-configured).
- Reading the file with getBytes() for arbitrarily large uploads.
- Thinking @RequestBody can parse multipart/form-data.