1. Introduction

In this tutorial, we'll focus on various mechanisms for sending multipart requests in Spring Boot. Multipart requests consist of sending data of various different types separated by a boundary as a part of a single HTTP method call.

Generally, we can send complicated JSON, XML, or CSV data as well as transfer multipart file(s) in this request. Examples of multipart files can be audio or an image file. Equally, we can also send simple key/value pair data with the multipart file(s) as a multipart request.

Let's look into various ways we can send this data.

2. Using @ModelAttribute

Let's consider a simple use case of sending an employee's data consisting of a name and a file using a form.

First, let's create an Employee abstraction to store the form data:

public class Employee {
    private String name;
    private MultipartFile document;
}

Next, let's generate the form using Thymeleaf:

<form action="#" th:action="@{/employee}" th:object="${employee}" method="post" enctype="multipart/form-data">
    <p>name: <input type="text" th:field="*{name}" /></p>
    <p>document:<input type="file" th:field="*{document}" multiple="multiple"/>
    <input type="submit" value="upload" />
    <input type="reset" value="Reset" /></p>
</form>

The important thing to note is that we declare the enctype as multipart/form-data in the view.

Finally, we'll create a method that accepts the form data, including the multipart file:

@RequestMapping(path = "/employee", method = POST, consumes = { MediaType.MULTIPART_FORM_DATA_VALUE })
public String saveEmployee(@ModelAttribute Employee employee) {
    employeeService.save(employee);
    return "employee/success";
}

Here, the two particularly important details are:

  • consumes attribute value is set to multipart/form-data
  • @ModelAttribute has captured all the form data into the Employee POJO, including the uploaded file

3. Using @RequestPart

This annotation associates a part of a multipart request with the method argument, which is useful for sending complex multi-attribute data as payload, e.g., JSON or XML.

Let's create a method with two arguments, first of type Employee and second as MultipartFile. Furthermore, we'll annotate both of these arguments with @RequestPart:

@RequestMapping(path = "/requestpart/employee", method = POST, consumes = { MediaType.MULTIPART_FORM_DATA_VALUE })
public ResponseEntity<Object> saveEmployee(@RequestPart Employee employee, @RequestPart MultipartFile document) {
    employee.setDocument(document);
    employeeService.save(employee);
    return ResponseEntity.ok().build();
}

Now, to see this annotation in action, let's create the test using MockMultipartFile:

@Test
public void givenEmployeeJsonAndMultipartFile_whenPostWithRequestPart_thenReturnsOK() throws Exception {
    MockMultipartFile employeeJson = new MockMultipartFile("employee", null,
      "application/json", "{\"name\": \"Emp Name\"}".getBytes());

    mockMvc.perform(multipart("/requestpart/employee")
      .file(A_FILE)
      .file(employeeJson))
      .andExpect(status().isOk());
}

Above, the important thing to note is that we've set the content type of the Employee part as application/JSON. Also, we're sending this data as a JSON file in addition to the multipart file.

Further details on how to test multipart requests can be found here.

4. Using @RequestParam

Another way of sending multipart data is to use @RequestParam. This is especially useful for simple data, which is sent as key/value pairs along with the file:

@RequestMapping(path = "/requestparam/employee", method = POST, consumes = { MediaType.MULTIPART_FORM_DATA_VALUE })
public ResponseEntity<Object> saveEmployee(@RequestParam String name, @RequestPart MultipartFile document) {
    Employee employee = new Employee(name, document);
    employeeService.save(employee);
    return ResponseEntity.ok().build();
}

Let's write the test for this method to demonstrate:

@Test
public void givenRequestPartAndRequestParam_whenPost_thenReturns200OK() throws Exception {
    mockMvc.perform(multipart("/requestparam/employee")
      .file(A_FILE)
      .param("name", "testname"))
      .andExpect(status().isOk());
}

5. Conclusion

In this article, we looked at how to effectively handle multipart requests in Spring Boot.

Initially, we sent multipart form data using a model attribute. Then we looked at how to separately receive multipart data using @RequestPart and @RequestParam annotations.

As always, the full source code is available over on GitHub.

Generic bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE
guest
0 Comments
Inline Feedbacks
View all comments