Multipart

After a MultipartResolver has been enabled, the content of POST requests with multipart/form-data is parsed and accessible as regular request parameters. The following example accesses one regular form field and one uploaded file:

  • Java

@Controller
public class FileUploadController {

  @PostMapping("/form")
  public String handleFormUpload(@RequestParam("name") String name,
      @RequestParam("file") MultipartFile file) {

    if (!file.isEmpty()) {
      byte[] bytes = file.getBytes();
      // store the bytes somewhere
      return "redirect:uploadSuccess";
    }
    return "redirect:uploadFailure";
  }
}

Declaring the argument type as a List<MultipartFile> allows for resolving multiple files for the same parameter name.

When the @RequestParam annotation is declared as a Map<String, MultipartFile> or MultiValueMap<String, MultipartFile>, without a parameter name specified in the annotation, then the map is populated with the multipart files for each given parameter name.

With Servlet multipart parsing, you may also declare jakarta.mockApi.http.Part instead of Infra MultipartFile, as a method argument or collection value type.

You can also use multipart content as part of data binding to a command object. For example, the form field and file from the preceding example could be fields on a form object, as the following example shows:

  • Java

class MyForm {

  private String name;

  private MultipartFile file;

  // ...
}

@Controller
public class FileUploadController {

  @PostMapping("/form")
  public String handleFormUpload(MyForm form, BindingResult errors) {
    if (!form.getFile().isEmpty()) {
      byte[] bytes = form.getFile().getBytes();
      // store the bytes somewhere
      return "redirect:uploadSuccess";
    }
    return "redirect:uploadFailure";
  }
}

Multipart requests can also be submitted from non-browser clients in a RESTful service scenario. The following example shows a file with JSON:

POST /someUrl
Content-Type: multipart/mixed

--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
Content-Disposition: form-data; name="meta-data"
Content-Type: application/json; charset=UTF-8
Content-Transfer-Encoding: 8bit

{
	"name": "value"
}
--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
Content-Disposition: form-data; name="file-data"; filename="file.properties"
Content-Type: text/xml
Content-Transfer-Encoding: 8bit
... File Data ...

You can access the "meta-data" part with @RequestParam as a String but you’ll probably want it deserialized from JSON (similar to @RequestBody). Use the @RequestPart annotation to access a multipart after converting it with an HttpMessageConverter:

  • Java

@PostMapping("/")
public String handle(@RequestPart("meta-data") MetaData metadata,
    @RequestPart("file-data") MultipartFile file) {
  // ...
}

You can use @RequestPart in combination with jakarta.validation.Valid or use Infra @Validated annotation, both of which cause Standard Bean Validation to be applied. By default, validation errors cause a MethodArgumentNotValidException, which is turned into a 400 (BAD_REQUEST) response. Alternatively, you can handle validation errors locally within the controller through an Errors or BindingResult argument, as the following example shows:

  • Java

@PostMapping("/")
public String handle(@Valid @RequestPart("meta-data") MetaData metadata, Errors errors) {
  // ...
}

If method validation applies because other parameters have @Constraint annotations, then HandlerMethodValidationException is raised instead. For more details, see the section on Validation.