How can I pass an object with an IFormFile property with FormData in an HTTP POST? (ASP.NET Core with React)

610 Views Asked by At

On the client side, I am accepting several groups of files. Each group of files is related to some other object. For example, let's say I want to allow users to upload several pictures for each Person in a directory. I would like to structure this as a list of dictionary-like objects that use the personID as a key, and a list of that Person's files as the value. {PersonID: id, Files: []}[] OR A list where each entry is a PersonID and a single File, if that's easier. {PersonID: id, File: pic1}[]

My problem comes with sending this data structure to the server-side through a POST. Objects need to be serialized into JSON strings, but Files cannot be serialized.

I'm already passing lists of Objects successfully, and I'm passing lists of Files successfully, but I don't know how to handle this hybrid scenario.

Client Side - React

const formData = new FormData();

// int
formData.append("divisionID", divisionID);

// Object List
listOfObjects.forEach(o => {
    formData.append("myObjectList[]", JSON.stringify(o)); //note the square brackets
});

// File List
listOfFiles.forEach(f => {
    formData.append("myFileList", f); //note the lack of square brackets
});

// What I'm trying to do that doesn't work
picturesToUpload.forEach(pic => {
    formData.append(
        "picturesToUpload[]",
         JSON.stringify({
             personID: pic.personID,
             file: pic.file
         })
     );
 });

// Request made to the backend api
// Send formData object
await fetch("uploadpictures", {
    method: "POST",
    body: formData
});

Server Side - ASP.NET Core

public class FileToUpload
{
    [JsonPropertyName("personID")]
    public int PersonID { get; set; }
    [JsonPropertyName("file")]
    public IFormFile? File { get; set; }
}

...
public class MyController
{
    [Route("uploadPictures")]
    [HttpPost]
    public ActionResult UploadPictures(
        [FromForm] int divisionID                    // This works
        , [FromForm] List<string> myObjectList       // This works
        , [FromForm] List<IFormFile> myFileList      // This works
        , [FromForm] List<FileToUpload> picturesToUpload)    // DOES NOT WORK
    {
        ...

On the client side, I have tried appending picturesToUpload to the formData both with and without square brackets. I have tried with and without JSON.stringify(). In all cases, on the server, it doesn't work; I receive an empty list with no serialization, or else the File property of my FileToUpload object is null, when I use serialization.

1

There are 1 best solutions below

2
Tiny Wang On

when we need to post file to the api we need to use formdata, but we don' have a list of form and the form not support an object. In your scenario, you said you want to allow users to upload several pictures for each Person. Then I think we'd better to modify all persons' pictures at the same time, we update each person one time. So that what we need in the API is receiving a model which have several properties and a List<IFormFile> like below.

public class Data {
    public string name { get; set; }
    public List<IFormFile> files { get; set; }
}

[HttpPost("GetString")]
public string GetString([FromForm] Data data)
{
    return "hello world";
}

Then in the client side, we should have a function to upload file.

handleClick(event) {
        const data = new FormData();
        data.append("name", this.state.userName);
        for (let i = 0; i < this.state.files.length; i++) {
            data.append("files", this.state.files[i]);
        }
        //data.append("files", this.state.files[1]);//FormData doesn't support a FileList object
        fetch('weatherforecast/getstring', {
            method: "POST",
            body: data
        });
    }

enter image description here