Problem encountered when attempting to send an array of files in two dimensions

When I send multiple files using formData, it's structured like this:

https://i.sstatic.net/BjePq.png

Within my Spring MVC Controller:

@PostMapping(value = "/marches")
public Integer saveMarches(
        @RequestPart("formJson") FooBean formJson, 
        @RequestPart("attachOs") MultipartFile[][] attachOs
        ) throws IOException {
    ...
}

My configuration is:

@Bean(name = "multipartResolver")
public CommonsMultipartResolver multipartResolver() {
    CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
    multipartResolver.setMaxUploadSize(30000000);
    return multipartResolver;
}

However, I encountered a 400 Bad Request error in the browser:

https://i.sstatic.net/18p0n.png

And in my IDE, I received the following error message:

Resolved [org.springframework.web.multipart.support.MissingServletRequestPartException:
Required request part 'attachOs' is not present]

When I tried using

@RequestPart("attachOs[][]") MultipartFile[][] attachOs
, I still received a bad request error with
Required request part 'attachOs[][]' is not present

The problem is clear: Spring is only looking for the attachOs part (@RequestPart("attachOs")), but I am sending attachOs[0][0], attachOs[0][1], and so on.

When I send just the formJson part without files, or if I send a single file using

@RequestPart("attachOs") MultipartFile attachOs
or an array of files using
@RequestPart("attachOs") MultipartFile[] attachOs
, everything works correctly.

Here is the JavaScript code I am using:

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,
})
...

The structure of my formJson is as follows:

{
    // form fields
    ...
    os: [
        {
            // os form fields
            ...
            attachment: [{ /* File type */ }, ...], // multiple files per os
        },
        ...
    ]
}

I am aware that files cannot be sent along with JSON, which is why I construct the formData as shown above, and then remove the attachment property from the JSON structure.

So, my questions are:

1. How can I resolve the bad request issue?

2. Is there an alternative approach or design pattern to handle this use case?

Answer №1

To enable the sending of multiple file attachments per operating system (OS), consider using a List rather than 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 {

    // Link files to their respective OS by utilizing the index in their name.
}

In your Angular script, remember to include the OS index in the file name.

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

Answer №2

After doing some research, I was able to find a resolution by employing a @ModelAttribute (source: here);

To begin, establish a model class as shown below;

public class MyRequest {
    private FooBean formJson;
    private MultipartFile[][] attachOs = new MultipartFile[2][2];

    // getters/setters
}

Next, integrate this model class into your controller like this;

@PostMapping(value = "/marches", consumes = "multipart/form-data")
public Integer saveMarches(@ModelAttribute MyRequest request) throws IOException {
    // code goes here
}

The crucial step was initializing the MultipartFile[][] attachOs, as without it, working with multidimensional arrays can lead to internal initialization issues.


Alternatively, you can utilize the following type within the model class;

private List<MultipartFile>[] attachOs;

This method functions without requiring explicit initialization.

Answer №3

Spring now has the capability to bind multipart and part files using multivalue maps and single value maps, as documented in SPR-17405

By adding Map<String, MultipartFile>, you can easily map multipart values to keys.

To reference these values, you can use syntax like attachOs[0][0], attachs[0][1]

@PostMapping(value = "/marches")
public Integer saveMarches(
        @RequestPart("formJson") FooBean formJson, 
        @RequestParam Map<String, MultipartFile> attachOs
        ) throws IOException {
    ...
}

Alternatively, if you want to send multiple multipart values per row, you can utilize

MultiValueMap<String, MultipartFile>
. Just remember to update your Angular code accordingly.

For this variation, you can reference values like attachOs[0], attachs[1]

 @PostMapping(value = "/marches")
    public Integer saveMarches(
            @RequestPart("formJson") FooBean formJson, 
            @RequestParam MultiValueMap<String, MultipartFile> attachOs
            ) throws IOException {
        ...
    }

If you opt for the second variant, you can assign a unique name to each multipart value and append the files accordingly. There is no need to treat it like an array. By using data.append multiple times with the same name, the files will be added as an array automatically.

for (const [i, os] of formJson.os.entries()) {
    if(os.attachment) {
        for (const [j, file] of [...os.attachment].entries()) {
            formData.append(os.name, file);
        }
    }
}

Answer №4

In order to handle custom conversions, a new @Component called custom converter must be created to implement the HttpMessageConverter interface.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Implementing a configuration file into a client-side web application using asynchronous methods

Currently, I am facing a challenge with an SPA that is being developed using various custom components from different sources. The main issue at hand is how to initialize certain settings (such as Endpoint URLs) using a settings file that can be configure ...

Reducer is unaware of my current state within react redux

I am currently developing an application that allows users to add items to a collection. The process involves the user inputting the name of the collection item, and the corresponding item is added to the collection. However, I encountered an issue when di ...

Limit the text input area in HTML to a fixed size, preventing any text from exceeding the specified boundary

Is there a way to create a fixed line text area that limits the user from typing beyond a certain number of lines and maximum width? My current CSS styling for this is as follows: .area-style { resize: none; width: 324px; height: 200px; m ...

There was an error during compilation: Module not detected - Unable to locate './xxxx'

Looking for help with importing a file from another folder into my Next.js project. I need assistance with the correct syntax and paths as I am encountering an error. Here is a link to the screenshot of the error: https://i.sstatic.net/jZ6kk.png Below are ...

Unable to retrieve the value of a textbox located within a gridview

In my grid view, I have a textbox nested inside a TemplateField. I want to retrieve the value of this textbox when a button is clicked: This is where I create the textbox: <ItemTemplate> <asp:TextBox runat="server" ID="txtEmail" Text=&a ...

Getting the Value from a Promise into a Variable in a React Component

I am currently immersed in a React project where I am utilizing the Axios library to retrieve data from an API and store it in a variable. After scouring the internet, it seems that the only method available is through Promise.then(), but this approach si ...

Javascript Google Maps API is not working properly when trying to load the Gmap via a Json

Trying to display a map using Gmaps and Jquery Ajax through JSON, but encountering difficulty in getting the map to appear on the page. The correct coordinates are being retrieved as confirmed by testing in the console. Puzzled by why it's not showin ...

Exploring the Differences Between Arrays of Objects and Arrays in JavaScript

While working on a project for a client, I encountered an interesting problem. I have two arrays - one with objects and one with values. The task is to populate the array of objects with new objects for every item in the value array. To clarify, here is th ...

After successfully building with Vite, an error occurs stating "TypeError: can't convert undefined to object." However, during development with Vite, everything functions flawlessly

Currently, I am utilizing Vite in conjunction with React and Typescript for my project. Interestingly, when I execute 'vite dev', the live version of the website works flawlessly without any errors showing up on the console. However, things take ...

Connect data dynamically to the p-table based on columns

I'm facing a challenge in displaying JSON data in a table format, and I'm looking for a way to accomplish this using p-table. When looping through the data, I'm noticing duplicate records in the rows. Can anyone guide me on how to achieve th ...

Exploring additional parameters with JAX-RS and Jackson integration

In my current project, I am utilizing JAX-RS, Jackson, and JPA. The JAX-RS resources are responsible for directly mapping incoming JSON to POJOs (JPA entities) that will be persisted. @POST @Consumes(MediaType.APPLICATION_JSON) public Response createEntit ...

No user was located using Mongoose.findOne()

Utilizing fetch() to make a call to a URL looks like this: const context = useContext(AuthContext); const navigate = useNavigate(); const handleSubmit = (event) => { event.preventDefault(); const dat ...

Exploring the `React.createRef` method using Enzyme for testing purposes

Is there a way to test the following class that utilizes the React.createRef API? I couldn't find any examples online. Has anyone attempted this before? How can I mock the ref effectively? My preference would be to utilize shallow. class Main exten ...

Ways to designate a tab as active

Having trouble styling the active tab in my tabbed menu created with Bootstrap. The active class seems to only affect the first tab. How can I make it work for all tabs? Check out this screenshot for reference. Below is the code snippet: <script ...

Javascript does not have the capability to parse JSON

Received a JSON response from a PHP file: [ { "color": "black", "product_name": "Prod2", "revision": "apps/" }, { "color": "green", "product_name": "Prod1", "revision": "dev/" } ] (successfu ...

Uncheck the box for disabling the bottom row of HTML tables

I need help with disabling a check box under certain conditions. Specifically, I want to disable the check box if there is only one row in the table or if it's the last row, but for some reason it's not working as expected. Here is the HTML: &l ...

Issue with Vue plugin syntax causing component not to load

I'm facing an issue with a Vue plugin that I have. The code for the plugin is as follows: import _Vue from "vue"; import particles from "./Particles.vue"; const VueParticles = (Vue: typeof _Vue, options: unknown) => { _Vue. ...

Executing a conditional onClick function when the page loads

I have implemented a php-based authorization system that loads different authorization levels into a JavaScript variable. My goal is to disable certain onclick events based on the user's authorization level. I've come up with the following code ...

How to Use JQuery to Display Elements with a Vague Name?

Several PHP-generated divs are structured as follows: <div style="width:215px;height:305px;background-color: rgba(255, 255, 255, 0.5);background-position: 0px 0px;background-repeat: no-repeat;background-size: 215px 305px;display:none;position:fixed;top ...

"Step-by-step guide to building a webgrid or table to organize and display your

These are the HTML codes I have created: <table> <tr> <td><label style="text-align:left">Product Code:</label></td> <td><input type="text" id="productCode" value="" /> </td> </tr> & ...