Reputation: 6236
I am sending multiple files with formData
like this
In my Spring MVC Controller
@PostMapping(value = "/marches")
public Integer saveMarches(
@RequestPart("formJson") FooBean formJson,
@RequestPart("attachOs") MultipartFile[][] attachOs
) throws IOException {
...
}
My conf :
@Bean(name = "multipartResolver")
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(30000000);
return multipartResolver;
}
But i got a 400 Bad Request
in the browser
And in the IDE I got :
Resolved [org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'attachOs' is not present]
and if i try @RequestPart("attachOs[][]") MultipartFile[][] attachOs
i got always a bad request with Required request part 'attachOs[][]' is not present
The problem is obvious : spring is searching just for attachOs
part (@RequestPart("attachOs")
) but i am sending attachOs[0][0]
, attachOs[0][1]
...
When i send just the formJson
part without files or if i send just a single file @RequestPart("attachOs") MultipartFile attachOs
or one dimension array of files @RequestPart("attachOs") MultipartFile[] attachOs
everything works fine.
Javascript code :
const formData = new FormData();
for (const [i, os] of formJson.os.entries()) {
if(os.attachment) {
for (const [j, file] of [...os.attachment].entries()) {
formData.append(`attachOs[${i}][${j}]`, file );
}
}
}
...
formData.append('formJson',
new Blob([JSON.stringify(formJson)], {type:'application/json'}));
...
axios({
url: ...,
method: 'POST',
data: formData,
})
...
My formJson
structure is
{
// form fields
...
os: [
{
// os form fields
...
attachment: [{ /* File type */ }, ...], // multiple files per os
},
...
]
}
I know that files cannot be sent along with JSON that's why i am constructing the formData above and after that i will delete the attachment property from JSON structure
So my questions :
1. How to fix the bad request issue ?
2. is there another approach or design pattern to handle this use case ?
Upvotes: 8
Views: 1871
Reputation: 6154
If you want to send multiple file attachments per OS you can use a List
instead of a 2-dimensional array in the spring controller.
@PostMapping(value = "/marches")
public Integer saveMarches(
@RequestPart("formJson") FooBean formJson,
@RequestPart("attachOs") List<MultipartFile> files
) throws IOException {
// Associate files with their os using the index in their name.
}
and in your angular code append the os index in the name of the file.
for (const [i, os] of formJson.os.entries()) {
if (os.attachment) {
for (const [j, file] of [...os.attachment].entries()) {
formData.append(`attachOs`, file, file.name + ":" + i );
}
}
}
Upvotes: 2
Reputation: 14688
I found a solution by utilizing a @ModelAttribute
(from here);
First create a model class like;
public class MyRequest {
private FooBean formJson;
private MultipartFile[][] attachOs = new MultipartFile[2][2];
// getters/setters
}
Then add it to your controller like;
@PostMapping(value = "/marches", consumes = "multipart/form-data")
public Integer saveMarches(@ModelAttribute MyRequest request) throws IOException {
// some code
}
The important part was the initialization of the MultipartFile[][] attachOs
, otherwise it does not work with multidimensional arrays due to some internal initialization issue.
Or you can use the following type in the model class;
private List<MultipartFile>[] attachOs;
which works without an initialization.
Upvotes: 1
Reputation: 75934
Spring supports binding all multivalue map and single value map of multipart and part files in SPR-17405
Adding Map<String, MultipartFile>
will map the multipart value to key.
Something like attachOs[0][0]
, attachs[0][1]
@PostMapping(value = "/marches")
public Integer saveMarches(
@RequestPart("formJson") FooBean formJson,
@RequestParam Map<String, MultipartFile> attachOs
) throws IOException {
...
}
Another variation would be send multiple multipart values per row. For that you could use
MultiValueMap<String, MultipartFile>
. For this variation you have to update your angular code.
Something like attachOs[0]
, attachs[1]
@PostMapping(value = "/marches")
public Integer saveMarches(
@RequestPart("formJson") FooBean formJson,
@RequestParam MultiValueMap<String, MultipartFile> attachOs
) throws IOException {
...
}
If you would like to use second variant you can give unique name per os and append files. There is no need to make it appear like array. You can call data.append multiple append with same name and it will add them as array of files.
for (const [i, os] of formJson.os.entries()) {
if(os.attachment) {
for (const [j, file] of [...os.attachment].entries()) {
formData.append(os.name, file);
}
}
}
Upvotes: 2
Reputation: 951
You need to create custom converter @Component, which implements HttpMessageConverter
Upvotes: 0