reactjsasp.net-core.net-coreformik

React formik multiple files upload


I'm using react formik and need to do multiple files upload inside formik submit and post to backedn .net core api.

I encountered a problem when post multiple upload file object to backend always get error 400.

The problem is not about react communicate with backend api, and if we only sent over the name/email, the app works fine.

Also, if I change the formik.setFieldValue('profileFiles', files) to formik.setFieldValue('profileFiles', e.target.files[0]) the app works but can get only one profile Files each time, but we want to get all files selected from frontend.

backend code

public class Person
{
    public string name { get; set; }
    public string email { get; set; }
    public ICollection<IFormFile> profileFiles { get; set; }
}

[HttpPost]
[Route("[action]")]
public IActionResult SavePerson([FromForm] Person person)
{
    /* logic code */
    return Ok("hit ok");
}

frontend code

const SavePerson = () => {

  const person : Person = {
    name: '',
    email: '',
    profileFiles: []
  }

  const formik = useFormik({
    initialValues: person,
    validationSchema: personSchema,
    onSubmit: (values) => {
      const formData = new FormData()
      for(let value in values) {
        formData.append(value, values[value])
      }
      for (let i = 0; i <= values.uploadFiles.length; i++) {
        formData.append(`profileFiles[${i}]`, values.profileFiles[i]);
      }
      axios.post(`${api}/SavePerson`, formData)
    }
  })

  return (
    <>
       <form onSubmit={formik.handleSubmit} encType='multipart/form-data'>
        <div>
          <label htmlFor="Select a file" className="m-2 p-6">Select Profiles:</label>
          <input type='file' id='profileFiles' multiple={true} name='profileFiles' onChange={(e) => {
            var files : [] = new Array()
            for (let i = 0; i < e.target.files.length; i++) {
              files.push(e.target.files[i])
            }
            formik.setFieldValue('profileFiles', files)
          }} />
        </div>

        <div className="flex flex-row justify-center w-64 m-6">
          <button type="submit" className='bg-blue-500 '>Submit</button>
        </div>

      </form>
    </>
  )
}

Solution

  • The 400 BadRequest might caused by different reasons and usually the the person you sent could not be recognized. Since I don't know the data bound in your FromForm, here is a sample I started with the React and Asp.Net Core template in VS.

    PersonController.cs

    [Route("api/[controller]")]
    [ApiController]
    public class PersonController : ControllerBase
    {
        [HttpPost]
        [Route("[action]")]
        public async Task<IActionResult> SavePerson([FromForm] Person person)
        {
            if (person.ProfileFiles != null)
            {
                ...
            }
            return Ok("Files uploaded successfully");
        }
    }
    

    App.jsx in front

    import './App.css';
    import React from 'react';
    import { useFormik } from 'formik';
    import * as Yup from 'yup';
    import axios from 'axios';
    
    const SavePerson = () => {
        const formik = useFormik({
            initialValues: {
                name: '',
                email: '',
                profileFiles: []
            },
            validationSchema: Yup.object({
                name: Yup.string().required('Required'),
                email: Yup.string().email('Invalid email address').required('Required')
            }),
            onSubmit: (values) => {
                const formData = new FormData();
                formData.append('name', values.name);
                formData.append('email', values.email);
                values.profileFiles.forEach((file) => {
                    formData.append('profileFiles', file);
                });
    
                axios.post('/api/person/SavePerson', formData, {
                    headers: {
                        'Content-Type': 'multipart/form-data',
                    },
                })
                    .then(response => {
                        console.log(response.data);
                    })
                    .catch(error => {
                        console.error(error);
                    });
            },
        });
    
        return (
            <form onSubmit={formik.handleSubmit} encType='multipart/form-data'>
                <div>
                    <label htmlFor="name">Name:</label>
                    <input
                        id="name"
                        name="name"
                        type="text"
                        onChange={formik.handleChange}
                        value={formik.values.name}
                    />
                    {formik.touched.name && formik.errors.name ? (
                        <div>{formik.errors.name}</div>
                    ) : null}
                </div>
    
                <div>
                    <label htmlFor="email">Email:</label>
                    <input
                        id="email"
                        name="email"
                        type="email"
                        onChange={formik.handleChange}
                        value={formik.values.email}
                    />
                    {formik.touched.email && formik.errors.email ? (
                        <div>{formik.errors.email}</div>
                    ) : null}
                </div>
    
                <div>
                    <label htmlFor="profileFiles">Select Profiles:</label>
                    <input
                        type="file"
                        id="profileFiles"
                        multiple={true}
                        name="profileFiles"
                        onChange={(e) => {
                            const filesArray = Array.from(e.target.files);
                            formik.setFieldValue('profileFiles', filesArray);
                        }}
                    />
                </div>
    
                <div>
                    <button type="submit">Submit</button>
                </div>
            </form>
        );
    };
    
    function App() {
        return (
            <div className="App">
                <h1>Upload Files</h1>
                <SavePerson />
            </div>
        );
    }
    
    export default App;
    

    Test

    enter image description here enter image description here enter image description here