@RequestBody MultipartFile[] submissions
should be
@RequestParam("file") MultipartFile[] submissions
The files are not the request body, they are part of it and there is no built-in HttpMessageConverter that can convert the request to an array of MultiPartFile.
You can also replace HttpServletRequest with MultipartHttpServletRequest, which gives you access to the headers of the individual parts.
@RequestBody MultipartFile[] submissions
should be
@RequestParam("file") MultipartFile[] submissions
The files are not the request body, they are part of it and there is no built-in HttpMessageConverter that can convert the request to an array of MultiPartFile.
You can also replace HttpServletRequest with MultipartHttpServletRequest, which gives you access to the headers of the individual parts.
You can simply use a controller method like this:
@RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
@ResponseBody
public ResponseEntity<?> uploadFile(
@RequestParam("file") MultipartFile file) {
try {
// Handle the received file here
// ...
}
catch (Exception e) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(HttpStatus.OK);
} // method uploadFile
Without any additional configurations for Spring Boot.
Using the following html form client side:
<html>
<body>
<form action="/uploadFile" method="POST" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" value="Upload">
</form>
</body>
</html>
If you want to set limits on files size you can do it in the application.properties:
# File size limit
multipart.maxFileSize = 3Mb
# Total request size for a multipart/form-data
multipart.maxRequestSize = 20Mb
Moreover to send the file with Ajax take a look here: http://blog.netgloo.com/2015/02/08/spring-boot-file-upload-with-ajax/
Spring Boot: post and process multipart/form-data request with multiple body parts - Stack Overflow
Spring Boot controller - Upload Multipart and JSON to DTO
java - Spring boot - method 'POST' is not supported (multipart/form-data) - Stack Overflow
java - Upload multipart/form-data file in Spring boot and REST API - Stack Overflow
Videos
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>
So I’ve been googling this for hours, and my colleague, the backend developer on my team, has as well; how do we parse a multipart/form-data payload looking like this?
name: Overridden file name concerns: 5211622c-11e9-45cd-ac1f-cde09c864a51 shares[0].id: c0b1697e-9639-409a-b09d-9fd7334f987f shares[1].id: 5211622c-11e9-45cd-ac1f-cde09c864a51 file: (binary)
Obviously, what we want to parse this to in Spring, is
{
name: "Overridden file name",
concerns: "5211622c-11e9-45cd-ac1f-cde09c864a51",
shares: [
{ id: "c0b1697e-9639-409a-b09d-9fd7334f987f" },
{ id: "5211622c-11e9-45cd-ac1f-cde09c864a51" }
],
file: ...
}
The values of fields with static names are readily accessible, whereas shares isn’t a name in the payload; there are only separate values with names that start with shares, followed by the nested structure annotations.
Now, here’s the twist: I really don’t want to encode this nested data structure as a JSON string in my web frontend, as I’m using a generic form field component which is also in use in forms without files, ie. forms where the payload is serialized and submitted as JSON. The serializer in use in these cases understands annotations such as brackets and dots in input names and converts values into corresponding data structures (which is a really common thing in the JavaScript eco system). Heck, most of them even distinguish between numbers and strings inside brackets, and either give you an array or an object, so there’s usually even no need for dot notation.
Is there a [simple?] way to achieve parsing such multipart payloads into meaningful, structured data in Spring?