skip to content

How do you enforce upload size limits in Spring, and how do you handle the resulting error?

level: seniorimportance: should knowfreq 45%

answer

  1. max-file-size (1MB) / max-request-size (10MB) defaults
  2. MaxUploadSizeExceededException extends MultipartException
  3. @ControllerAdvice -> 413 Payload Too Large
  4. file-size-threshold = spool-to-disk cutoff
  5. align container maxSwallowSize with Spring limits

basics

~10 s

Set spring.servlet.multipart.max-file-size and max-request-size (defaults 1MB / 10MB). Exceeding them throws MaxUploadSizeExceededException, which you catch in a @ControllerAdvice/@ExceptionHandler to return a clean 413/400 response.

solid answer

~40 s

Two Spring Boot properties cap uploads: spring.servlet.multipart.max-file-size (per file, default 1MB) and max-request-size (total request, default 10MB). file-size-threshold controls when a part spools to disk vs memory, and location sets the temp directory. When a limit is exceeded during parsing, Spring raises MaxUploadSizeExceededException (a MultipartException). Handle it centrally with @ExceptionHandler(MaxUploadSizeExceededException.class) in a @ControllerAdvice and return a meaningful status (413 Payload Too Large or 400). Be aware the servlet container has its own limits (e.g., Tomcat max-http-form-post-size / maxSwallowSize) that can abort the connection before Spring's handler runs, so align container and Spring settings. Also note errors may surface after the response has partly started if not parsed eagerly.

code

java · 24 lines
java
// application.yml
// spring.servlet.multipart:
//   max-file-size: 25MB
//   max-request-size: 30MB
//   file-size-threshold: 1KB
//   location: /var/app/tmp

@RestControllerAdvice
public class UploadErrorHandler {

    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public ResponseEntity<Map<String, Object>> handleTooLarge(MaxUploadSizeExceededException ex) {
        long max = ex.getMaxUploadSize(); // -1 if unknown
        return ResponseEntity.status(HttpStatus.PAYLOAD_TOO_LARGE)
                .body(Map.of("error", "FILE_TOO_LARGE", "maxBytes", max));
    }

    // MaxUploadSizeExceededException is a MultipartException; catch the parent
    // for other multipart parse failures if desired.
    @ExceptionHandler(MultipartException.class)
    public ResponseEntity<Map<String, Object>> handleMultipart(MultipartException ex) {
        return ResponseEntity.badRequest().body(Map.of("error", "BAD_MULTIPART"));
    }
}

go deeper

for a junior

Know the two size properties and their defaults (1MB / 10MB).

for a middle

Add MaxUploadSizeExceededException handling via @ControllerAdvice returning 413.

for a senior

Explain file-size-threshold, temp location, and container-vs-Spring limit interaction.

for a principal

Design a DoS-resistant upload policy: aligned container limits, streaming, temp-dir capacity/permissions, and a clean error contract.

## Why limits matter Unbounded uploads let a client exhaust disk, memory, or bandwidth (a denial-of-service vector). Spring/Boot imposes conservative defaults and lets you tune them. ## The properties (`spring.servlet.multipart.*`) - **`max-file-size`** — maximum size of a **single** uploaded file. Default **1MB**. `-1` means unlimited. - **`max-request-size`** — maximum size of the **entire** multipart request (all parts + overhead). Default **10MB**. `-1` means unlimited. This is the effective ceiling for multi-file forms. - **`file-size-threshold`** — the size at which an in-progress part is **spooled to disk** instead of held in memory. Default **0B** = always write to disk. Raising it (e.g., `2KB`) keeps tiny parts in memory. - **`location`** — directory for temp files. Defaults to the container temp dir. Point it at a volume with enough space and correct permissions. - **`max-file-size` vs container**: with `StandardServletMultipartResolver`, the **container** enforces sizes via the `MultipartConfigElement`. Some containers *also* have separate caps (Tomcat `maxSwallowSize`, `maxPostSize`; and `server.tomcat.max-http-form-post-size` for non-multipart). These can trigger before/independently of Spring. ## The exception When a size limit is exceeded during multipart parsing, Spring throws **`org.springframework.web.multipart.MaxUploadSizeExceededException`**, a subclass of **`MultipartException`**. With eager parsing (the default, `resolve-lazily=false`), this happens **before** your handler executes. ### Handling it ```java @ControllerAdvice class UploadExceptionHandler { @ExceptionHandler(MaxUploadSizeExceededException.class) ResponseEntity<ApiError> tooLarge(MaxUploadSizeExceededException ex) { return ResponseEntity.status(HttpStatus.PAYLOAD_TOO_LARGE) .body(new ApiError("FILE_TOO_LARGE", ...)); } } ``` - `413 Payload Too Large` is the semantically correct status; some teams use `400`. - Without a handler, Spring Boot's default error handling returns a 500-ish error. ## Gotchas - **Container aborts the connection**: if the request exceeds the container's own swallow limit (Tomcat `maxSwallowSize`), the container may reset the connection so the client never gets your neat JSON error. Keep container limits ≥ Spring limits (or aligned) so *Spring* rejects it gracefully. - **`resolve-lazily=true`**: parsing (and thus the size check/exception) is deferred until a part is first accessed — the exception then surfaces inside your handler, possibly after you've begun writing a response. - **`file-size-threshold` too high** under load → parts held in heap → memory pressure. Default (disk) is safer for large files. - **Temp dir** must exist, be writable, and have capacity; a full/unwritable `location` causes IOExceptions mid-upload. - Multipart limits don't cap *non-multipart* POST bodies — those are governed by container settings like `server.tomcat.max-http-form-post-size`. ## When to tune Raise limits for legitimate large-file features (video, datasets), but pair with streaming to storage, a container limit that matches, and an explicit `MaxUploadSizeExceededException` handler for a clean client contract.

  • You set max-file-size to 100MB but Tomcat still resets the connection on large uploads — why?
    The container has independent limits (e.g., Tomcat maxSwallowSize / connector caps). If the request exceeds those, the container aborts before Spring's handler runs, so the client never sees a 413. Raise/align the container limits with Spring's.
  • What does file-size-threshold control, and what's the risk of setting it very high?
    It's the size above which a part is spooled to disk instead of kept in memory (default 0 = always disk). A high threshold keeps large parts in heap, risking OutOfMemoryError under concurrent uploads.
  • Which exception type do you catch, and what's its parent?
    MaxUploadSizeExceededException, a subclass of MultipartException. Catch it in @ControllerAdvice to return 413/400; catching MultipartException covers broader parse failures.

saying these in an interview costs you the question

  • Thinking Spring's max-request-size caps the size of every file individually (it caps the whole request; max-file-size caps each file).
  • Assuming a MaxUploadSizeExceededException handler always reaches the client even when the container aborts the connection.
  • Believing the default file-size-threshold keeps everything in memory (default 0 = disk).
  • Confusing multipart limits with server.tomcat.max-http-form-post-size (non-multipart).

context