Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issue with sending 2 dimensional array of files

I am sending multiple files with formData like this

form data structure

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

enter image description here

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 ?

like image 811
Hayi Avatar asked Sep 25 '19 16:09

Hayi


2 Answers

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 );
        }
    }
}
like image 98
Mustafa Avatar answered Sep 22 '22 21:09

Mustafa


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.

like image 33
buræquete Avatar answered Sep 22 '22 21:09

buræquete