Spring Boot controller - Upload Multipart and JSON to DTO

Arian :

I want to upload a file inside a form to a Spring Boot API endpoint.

The UI is written in React:

export function createExpense(formData) {
  return dispatch => {
    axios.post(ENDPOINT,
      formData, 
      headers: {
        'Authorization': //...,
        'Content-Type': 'application/json'
      }
      ).then(({data}) => {
        //...
      })
      .catch(({response}) => {
        //...
      });
    };
}

  _onSubmit = values => {
    let formData = new FormData();
    formData.append('title', values.title);
    formData.append('description', values.description);
    formData.append('amount', values.amount);
    formData.append('image', values.image[0]);
    this.props.createExpense(formData);
  }

This is the java side code:

@RequestMapping(path = "/{groupId}", method = RequestMethod.POST)
public ExpenseSnippetGetDto create(@RequestBody ExpensePostDto expenseDto, @PathVariable long groupId, Principal principal, BindingResult result) throws IOException {
   //..
}

But I get this exception on the Java side:

org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'multipart/form-data;boundary=----WebKitFormBoundaryapHVvBsdZYc6j4Af;charset=UTF-8' not supported

How should I resolve this issue? The similar API endpoints and JavaScript side code is already working.

Note

I've seen a solution where it suggests that the request body should have 2 attributes: one which the JSON section goes under, another for the image. I'd like to see if it is possible to have it automatically converted to DTO.


Update 1

The upload payload sent by the client should be converted to the following DTO:

public class ExpensePostDto extends ExpenseBaseDto {

    private MultipartFile image;

    private String description;

    private List<Long> sharers;

}

So you can say it's a mix of JSON and multipart.


Solution

The solution to the problem is to use FormData on the front-end and ModelAttribute on the backend:

@RequestMapping(path = "/{groupId}", method = RequestMethod.POST,
        consumes = {"multipart/form-data"})
public ExpenseSnippetGetDto create(@ModelAttribute ExpensePostDto expenseDto, @PathVariable long groupId, Principal principal) throws IOException {
   //...
}

and on the front-end, get rid of Content-Type as it should be determined by the browser itself, and use FormData (standard JavaScript). That should solve the problem.

UsamaAmjad :

Yes, you can simply do it via wrapper class.

1) Create a Class to hold form data:

public class FormWrapper {
    private MultipartFile image;
    private String title;
    private String description;
}

2) Create an HTML form for submitting data:

<form method="POST" enctype="multipart/form-data" id="fileUploadForm" action="link">
    <input type="text" name="title"/><br/>
    <input type="text" name="description"/><br/><br/>
    <input type="file" name="image"/><br/><br/>
    <input type="submit" value="Submit" id="btnSubmit"/>
</form>

3) Create a method to receive form's text data and multipart file:

@PostMapping("/api/upload/multi/model")
public ResponseEntity<?> multiUploadFileModel(@ModelAttribute FormWrapper model) {
    try {
        // Save as you want as per requiremens
        saveUploadedFile(model.getImage());
        formRepo.save(mode.getTitle(), model.getDescription());
    } catch (IOException e) {
        return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
    }

    return new ResponseEntity("Successfully uploaded!", HttpStatus.OK);
}

4) Method to save file:

private void saveUploadedFile(MultipartFile file) throws IOException {
    if (!file.isEmpty()) {
        byte[] bytes = file.getBytes();
        Path path = Paths.get(UPLOADED_FOLDER + file.getOriginalFilename());
        Files.write(path, bytes);
    }
}

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=426805&siteId=1