2

I have a React component in which a user can upload an unlimited number of images, which are collected in an array.

I am currently using FormData to upload these images, along with some other text fields.

However I want to get away from using FormData. Is there a better way to upload data like an array of images with plain Axios?

Component's State After 3 Image Uploads

this.state.files = [
   // image file 0,
   // image file 1,
   // image file 2
];

Current Axios Function - Using FormData

let formData = new FormData();

this.state.files.forEach(file => formData.append('files[]',file));

let headers = { 'Content-Type': "multipart/form-data; charset=utf-8; boundary=" + Math.random().toString().substr(2) };

axios.post('/api/upload-images',formData,{headers: headers});

Desired Axios Function - No FormData

let headers = { 'Content-Type': "multipart/form-data; charset=utf-8; boundary=" + Math.random().toString().substr(2) };

axios.post('/api/upload-images',{...this.state},{headers: headers});

When I try to do it in the desired way, the files on received by the server are empty.

1

1 Answer 1

4

You could create a custom axios instance and set the transformRequest config parameter to a function that converts the data to a form when the multipart/form-data content type header is present. This would look something like the following. Disclaimer: I have not tested this.

// put formAxios in its own module to reuse it across the project
export const formAxios = axios.create({
    transformRequest: [function (data, headers) {
        if (headers['Content-Type'] && headers['Content-Type'].startsWith('multipart/form-data')) {
            const form = new FormData();
            for (const key in data) {
                const value = data[key];
                if (Array.isArray(value)) {
                    const arrayKey = `${key}[]`;
                    value.forEach(v => {
                        form.append(arrayKey, v);
                    });
                } else{
                    form.append(key, value);
                }
            }
            return form;
        }

        return data;
    }],
});

Calling this would be exactly like your last sample, but with the custom axios instance:

let headers = { 'Content-Type': "multipart/form-data; charset=utf-8; boundary=" + Math.random().toString().substr(2) };

formAxios.post('/api/upload-images', {...this.state}, {headers});
Sign up to request clarification or add additional context in comments.

1 Comment

This is a great solution. I don't mind making this module once and then reusing it as you suggested.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.