Spring Boot controller - Upload Multipart and JSON to DTO

I had a similar use case where I had some JSON data and image upload (Think of it as a user trying to register with a personal details and profile image).

Referring to @Stephan and @GSSwain answer I came up with a solution with Spring Boot and AngularJs.

Below is a snapshot of my code. Hope it helps someone.

    var url = "https://abcd.com/upload";
    var config = {
        headers : {
            'Content-Type': undefined
        }

    }
    var data = {
        name: $scope.name,
        email: $scope.email
    }
    $scope.fd.append("obj", new Blob([JSON.stringify(data)], {
                type: "application/json"
            }));

    $http.post(
        url, $scope.fd,config
    )
        .then(function (response) {
            console.log("success", response)
            // This function handles success

        }, function (response) {
            console.log("error", response)
            // this function handles error

        });

And SpringBoot controller:

@RequestMapping(value = "/upload", method = RequestMethod.POST, consumes = {   "multipart/form-data" })
@ResponseBody
public boolean uploadImage(@RequestPart("obj") YourDTO dto, @RequestPart("file") MultipartFile file) {
    // your logic
    return true;
}

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);
    }
}

I had created a similar thing using pure JS and Spring Boot. Here is the Repo. I'm sending an User object as JSON and a File as part of the multipart/form-data request.

The relevant snippets are below

The Controller code

@RestController
public class FileUploadController {

    @RequestMapping(value = "/upload", method = RequestMethod.POST, consumes = { "multipart/form-data" })
    public void upload(@RequestPart("user") @Valid User user,
            @RequestPart("file") @Valid @NotNull @NotBlank MultipartFile file) {
            System.out.println(user);
            System.out.println("Uploaded File: ");
            System.out.println("Name : " + file.getName());
            System.out.println("Type : " + file.getContentType());
            System.out.println("Name : " + file.getOriginalFilename());
            System.out.println("Size : " + file.getSize());
    }

    static class User {
        @NotNull
        String firstName;
        @NotNull
        String lastName;

        public String getFirstName() {
            return firstName;
        }

        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }

        public String getLastName() {
            return lastName;
        }

        public void setLastName(String lastName) {
            this.lastName = lastName;
        }

        @Override
        public String toString() {
            return "User [firstName=" + firstName + ", lastName=" + lastName + "]";
        }

    }
}

The HTML and JS code

<html>    
<head>
    <script>
        function onSubmit() {

            var formData = new FormData();

            formData.append("file", document.forms["userForm"].file.files[0]);
            formData.append('user', new Blob([JSON.stringify({
                "firstName": document.getElementById("firstName").value,
                "lastName": document.getElementById("lastName").value
            })], {
                    type: "application/json"
                }));
            var boundary = Math.random().toString().substr(2);
            fetch('/upload', {
                method: 'post',
                body: formData
            }).then(function (response) {
                if (response.status !== 200) {
                    alert("There was an error!");
                } else {
                    alert("Request successful");
                }
            }).catch(function (err) {
                alert("There was an error!");
            });;
        }
    </script>
</head>

<body>
    <form name="userForm">
        <label> File : </label>
        <br/>
        <input name="file" type="file">
        <br/>
        <label> First Name : </label>
        <br/>
        <input id="firstName" name="firstName" />
        <br/>
        <label> Last Name : </label>
        <br/>
        <input id="lastName" name="lastName" />
        <br/>
        <input type="button" value="Submit" id="submit" onclick="onSubmit(); return false;" />
    </form>
</body>    
</html>