Using ajax to post a view model which includes an IFormFile property in MVC Core

11,104

Unfortunately the jQuery serialize() method will not include input file elements. So file user selected is not going to be included in the serialized value.

What you may do is, create a FormData object, append the files to that. You need to append the form field values as well to this same FormData object. You may simply loop through all the input field and add it. Also, in the ajax call, you need to specify processData and contentType property values to false

This should work

$(function () {

    $("form").submit(function (e) {
        e.preventDefault();

        var formAction = $(this).attr("action");
        var fdata = new FormData();

        var fileInput = $('#File')[0];
        var file = fileInput.files[0];
        fdata.append("File", file);

        // You can update the jquery selector to use a css class if you want
        $("input[type='text'").each(function (x, y) {
            fdata.append($(y).attr("name"), $(y).val());
        });

        $.ajax({
            type: 'post',
            url: formAction ,
            data: fdata,
            processData: false,
            contentType: false
        }).done(function(result) {
            // do something with the result now
            console.log(result);
        });

    });

 });

The above code is reading the action attribute from the form ( which was submitted) and using that as the url for the ajax form submit.

@model YourViewModel
<form asp-action="Index" asp-controller="Home" method="post" >

    <label asp-for="DocumentOwner"></label>
    <input type="text" asp-for="DocumentOwner" />

    <label asp-for="DocumentTypeId"></label>
    <input type="text" asp-for="DocumentTypeId" />

    <label asp-for="File"></label>
    <input asp-for="File" />

    <input type="submit"  />
</form>

In your HttpPost action method, you can use the same view model as the parameter. The below sample code read the File property and save it to the uploads directory inside the wwwroot directory.

public class HomeController : Controller
{
    private readonly IHostingEnvironment hostingEnvironment;
    public HomeController(IHostingEnvironment environment)
    {
        hostingEnvironment = environment;
    }
    [HttpPost]
    public IActionResult Index(YourViewModel vm)
    {
        if (ModelState.IsValid)
        {
            if (vm.File != null)
            {
                var uploads = Path.Combine(hostingEnvironment.WebRootPath, "uploads");
                var filePath = Path.Combine(uploads, GetUniqueFileName(vm.File.FileName));
                vm.File.CopyTo(new FileStream(filePath, FileMode.Create));
                return Json(new {status = "success", message = "Successfully saved"});
            }
        }
        else
        {
            // handle what to do when model validation fails
        }
        return Json(new {status = "error", message = "something wrong!"});
    }
    private string GetUniqueFileName(string fileName)
    {
        fileName = Path.GetFileName(fileName);
        return  Path.GetFileNameWithoutExtension(fileName)
                  + "_" + Guid.NewGuid().ToString().Substring(0, 4) 
                  + Path.GetExtension(fileName);
    }
}
Share:
11,104
lakeside
Author by

lakeside

Updated on June 17, 2022

Comments

  • lakeside
    lakeside almost 2 years

    I'm trying to post a simple viewmodel in MVC core using a jquery AJAX post, however the IFormFile property seems to be null on the model.

    I have the following view model:

        [Required]
        public IFormFile File { get; set; }
    
        [Required]
        [Display(Name = "Owner")]
        public int DocumentOwner { get; set; }
    
        [Required]
        [Display(Name = "Document Type")]
        public int DocumentTypeId { get; set; }
    

    I then use a jQuery ajax post inside a script at the bottom of my view:

    $("form").submit(function (e) {
        e.preventDefault();
    
        var model = $('form').serialize();
    
        $.ajax({
            type: "POST",
            url: "/Client/AddDocument",
            contentType: false,
            processData: false,
            data: model,
            success: function (message) {
                alert(message);
            },
            error: function () {
                alert("There was error uploading files!");
            }
        });
    });
    

    The viewmodel is successfully passing the other values to the controller, however the file is coming through as null.

    Anyone able to help?