Upload multiple files with parameters in ASP.NET Core

11,267

Your approach with using _UploadForm generates the following html (let's focus on input's only since this is the most important part)

<input class="form-control" name="Title" />
<input class="form-control" name="Description" />
<input type="file" name="File" />

...
<input class="form-control" name="Title" />
<input class="form-control" name="Description" />
<input type="file" name="File" />

... and so on

So name attributes contains only FileInfo model's properties names without indexes and this is only suitable for the case when your controller expects single model

public IActionResult Upload(FileInfo vm)

And in order to make your html work with your current controller with list of models

public IActionResult Upload(List<FileInfo> vm)

It should look like this

<!-- version 1 -->
<input class="form-control" name="[0].Title" />
<input class="form-control" name="[0].Description" />
<input type="file" name="[0].File" />

...
<input class="form-control" name="[1].Title" />
<input class="form-control" name="[1].Description" />
<input type="file" name="[1].File" />

... and so on

Or

<!-- version 2 -->
<!-- the name before index must match parameter name in controller -->
<input class="form-control" name="vm[0].Title" />
<input class="form-control" name="vm[0].Description" />
<input type="file" name="vm[0].File" />

...
<input class="form-control" name="[1].Title" />
<input class="form-control" name="[1].Description" />
<input type="file" name="vm[1].File" />

... and so on

This is possible to accomplish using tag helpers and partial view in slightly different way. All you need to do is turn partial view's model to list and update asp-for expressions.

_UploadForm.cshtml

@model List<SessionStateTest.Models.FileInfo>

@for (int i = 0; i < Model.Count; i++)
{
    <div class="form-group">
        <label>Title</label>
        <input class="form-control" asp-for="@Model[i].Title" />
    </div>
    <div class="form-group">
        <label>Description</label>
        <input class="form-control" asp-for="@Model[i].Description" />
    </div>
    <div class="form-group">
        <label></label>
        <input type="file" asp-for="@Model[i].File" />
    </div>
}

View

<form asp-action="Upload" asp-controller="Home" method="Post" enctype="multipart/form-data">

    @await Html.PartialAsync("_UploadForm", Model.Upload)

    <div class="col-xs-12">
        <input type="submit" value="Upload" class="btn btn-sm btn-info" />
    </div>
</form>

It will generate html like in version 1.

Share:
11,267
Niroda
Author by

Niroda

Updated on June 05, 2022

Comments

  • Niroda
    Niroda almost 2 years

    My view model:

    public class FileInfo
    {
        [Required]
        [StringLength(50, ErrorMessage = "TitleErrorMessage", MinimumLength = 2)]
        public string Title { get; set; }
    
        [Required]
        [StringLength(100, ErrorMessage = "DesErrorMessage", MinimumLength = 3)]
        public string Description { get; set; }
    
        [Required]
        [DataType(DataType.Upload)]
        public IFormFile File { get; set; }
    }
    

    The following is _UploadForm partial view file:

    @model SessionStateTest.Models.FileInfo
    
    <div class="form-group">
        <label>Title</label>
        <input class="form-control" asp-for="Title" />
    </div>
    <div class="form-group">
        <label>Description</label>
        <input class="form-control" asp-for="Description" />
    </div>
    <div class="form-group">
        <label></label>
        <input type="file" asp-for="File" />
    </div>

    That is used in another View with this code:

    <form asp-action="AddUploadForm" asp-controller="Home" method="Post">
        <input type="submit" value="Add another file" class="btn btn-sm btn-primary" />
    </form>
    <form asp-action="Upload" asp-controller="Home" method="Post" enctype="multipart/form-data">
    
        @foreach (var item in Model.Upload)
        {
            @(await Html.PartialAsync("_UploadForm", item))
        }
    
        <div class="col-xs-12">
            <input type="submit" value="Upload" class="btn btn-sm btn-info" />
        </div>
    </form>

    Basically AddUploadForm action adds a new view model of type FileInfo to Model.Upload which is my main view model. The problem is that the list List<FileInfo> vm in Upload action below is totally empty:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public IActionResult Upload(List<FileInfo> vm)
    {
        .... some other logic 
        return View();
    }
    

    I don't want to use multiple attribute because I would like to force user to provide a title and description for every file.

    Any help is kindly appreciated!

  • Niroda
    Niroda about 5 years
    Thank you! that solved the problem. But may I ask why it didn't work when I tried name="Title[]"? As far as I know this should work or did I miss something?
  • Alexander
    Alexander about 5 years
    @Niroda I believe this works for PHP but not for ASP.NET :)