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 ?
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 ?
Share Improve this question edited Oct 6, 2019 at 12:26 Hayi asked Sep 25, 2019 at 16:12 HayiHayi 6,32630 gold badges84 silver badges154 bronze badges 15-
Can you first try
@RequestPart(name="attachOs", required=false)
and test that your controller can accept onlyformJson
portion? Also addconsumes = "multipart/form-data"
in your@PostMapping
annotation – buræquete Commented Oct 3, 2019 at 9:26 -
I think the problem is that spring is looking for a part named
attachOs
but your form data shows part namedattachOs[0][0]
, etc. Don't know if spring could make a research by dynamic names. But maybe the solution is that you use attachOs as entry nam for each entry? Like:formData.append(
attachOs, file );
– Mohicane Commented Oct 3, 2019 at 9:26 -
try
@RequestPart("attachOs[][]") MultipartFile[][] attachOs
notice the square brackets in the annotation value. – Mustafa Commented Oct 3, 2019 at 9:31 -
How about trying with a single object, make it a
MultipartFile attachOs
with only one input from front-end withformData.append('attachOs', file );
only once, and try to see that you can essentially get multiple request parts correctly. – buræquete Commented Oct 3, 2019 at 9:36 -
@Mustafa
@RequestPart("attachOs[][]") MultipartFile[][] attach
already try it but with no success – Hayi Commented Oct 3, 2019 at 9:42
4 Answers
Reset to default 2Spring 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);
}
}
}
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 );
}
}
}
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.
You need to create custom converter @Component, which implements HttpMessageConverter