IFormFile always null (ASP.NET Core with MVC/Razor)

Viewed 25814

I have an ASP.NET Core MVC app attempting to upload an IFormFile. However, the IFormFile is always null. None of the other solutions I've found have solved this issue. What am I doing wrong?

Model

public class EmailForm
{
    [Display(Name = "Add a picture")]
    [DataType(DataType.Upload)]
    [FileExtensions(Extensions = "jpg,png,gif,jpeg,bmp,svg")]
    public IFormFile SubmitterPicture { get; set; }
}

Controller

public async Task<ActionResult> Contribute([Bind("SubmitterPicture")] EmailForm model)
{
    if (ModelState.IsValid)
    {
        //Do some stuff
    }
}

View

        <form method="post" asp-action="Contribute" asp-antiforgery="true" enctype="multipart/form-data" >
            <div class="form-group" >
                <div class="col-md-2">@Html.LabelFor(m => m.SubmitterPicture)</div>
                <div class="col-md-4">
                    <input type="file" name="SubmitterPicture" id="SubmitterPicture" />
    </div>
            <div class="form-group">
                <div class="col-md-offset-2 col-md-10">
                    <input type="submit" class="btn btn-default" value="Contribute" />
                </div>
            </div>
8 Answers
<form method="post" enctype="multipart/form-data">
</form>

enctype="multipart/form-data"

The multipart form data is the key.

You can alternatively get the file from the HttpContext.Request.Form.Files and get rid of the IFormFile interface in your model. I recommend this method as I believe that files have nothing to do with data models.

The example would be:

public IActionResult Index()
{
    //Please note that if no form data is posted
    // HttpContext.Request.Form will throw an exception
    if (HttpContext.Request.Form.Files[0] != null) {
        var file = HttpContext.Request.Form.Files[0];
        using (FileStream fs = new FileStream("Your Path", FileMode.CreateNew, FileAccess.Write, FileShare.Write)) {
            file.CopyTo(fs);
        }
    }
    return View();
}

If this method also fails, that means there is something wrong with the multipart request.

I found that by providing names on my html form, I was able to map to parameters (ASP.NET Core 2.1):

Client side:

<form method="post" enctype="multipart/form-data" action="...">
    <input type="file" name="myFile" required />
    <input type="password" name="myPass" required />
    <input type="submit" value="post" />
</form>

Server side:

[HttpPost()]
public async Task<IActionResult> Post(IFormFile myFile, [FromForm]string myPass)
{
    //...
}

Your code looks perfectly fine and it should work as long as you are using the same version of code you posted on the question. I have a very strong feeling that you are getting false for the ModelState.IsValid expression and hence seeing the some sort of unexpected behavior.

The FileExtensions data annotation is supposed to be used with String type properties, not with IFormFile type properties. Because of this reason, the IsValid returns false.

So remove that from your view model.

public class EmailForm
{
    [Display(Name = "Add a picture")]
    [DataType(DataType.Upload)]
    public IFormFile SubmitterPicture { get; set; }
}

Here is a relevant GH issue and explanation from one of the team member, for your reference.

FileExtensions Data annotation invalid ModelState #5117

Similarly, if anyone working in .Net Core 2.1 and are using asp-for inorder bind the model for your input elements, then do not give name & id properties for that <input> elements. Ideally, the InputTagHelper upon the rendering on the browser generates the name and id properties with their values. If you give the same value for name & id w.r.t Model class, then everything works fine. Else, system doesn't know to which model property it should bind. Better approach is not to give id and name on the <input> Below is the sample code.

<form id="uploadForm" enctype="multipart/form-data" name="uploadForm" asp-action="UploadExcel" method="post">

                <div class="form-group form-group-lg form-group-sm row ">
                    <div class="col-sm-12 col-md-10 col-lg-10 uploadDiv" style="display: flex !important">
                        <label asp-for="FileName" class="col-sm-12 col-md-10 col-lg-10" style="font-size: 15px; max-width: fit-content ">File Name :</label>
                        <input asp-for="FileName" class="form form-control fileName"
                               type="text"
                               placeholder="Enter your file name" />
<!--File upload control-->
                        <label asp-for="FileName" class="col-sm-12 col-md-10 col-lg-10" style="font-size: 15px; max-width: fit-content ">Attachment :</label>
                        <input asp-for="File" required class="form-control" type="file" placeholder="File Name" />
                    </div>
                </div>
                <div class=" form-group form-group-lg form-group-sm row">
                    <span asp-validation-for="FileName" class="text-danger"></span>
                </div>
                <div class=" form-group form-group-lg form-group-sm row">
                    <small>Please upload .xls or .xlxs or json or xml formatted files only</small>
                </div>
                <div class="form-group form-group-lg form-group-sm row">
                    <div class="col-sm-12 col-md-10 col-lg-10">
                        <input type="submit" class="btn btn-primary" name="submit" id="fileUploadButton" value="Upload" />
                        <input type="reset" class="btn btn-Gray" name="result" id="resetButton" value="Reset" />
                    </div>
                </div>
                <a asp-action="DownloadTemplate" asp-controller="Download" title="Click to download template">Import Batch Transactions Template</a>
            </form>

Model.cs

public class ExcelUploadViewModel
    {

        /// <summary>
        /// Gets or Sets the FileName
        /// </summary>
        [Required(ErrorMessage = "FileName is required")]
        public string FileName { get; set; }

        [Required(ErrorMessage = "File is required")]
        [DataType(DataType.Upload)]
        [FromForm(Name = "File")]
        public IFormFile File { get; set; }
}

Upon Submitenter image description here

Thank you.

I had this issue and the fix was to make sure the element of "input type="file"" had an id and name set. Then, in the controller, set the IFormFile parameter name to be exactly the same, case as well. G-luck

Adding the below to my controller fixed the issue for me. It was because my file was large. Adjust the limits as needed.

 [RequestFormLimits(ValueLengthLimit = int.MaxValue, MultipartBodyLengthLimit = int.MaxValue)]
Related