1

Im using ASP.NET MVC5. My goal was to upload file using jQuery ajax. When successful, return a partial view of the uploaded files in a table. When I step through the jQuery code, the id and files are correct, but when stepped into Action, both parameters are null. Here are my codes:

Controller's Action:

[HttpPost]
        public ActionResult AddAttachments(string id, IEnumerable< HttpPostedFileBase> files)
        {            
            if (files != null)
            {
                //save files
            }
            var cardKey = db.CardKeys.Single(s => s.CardKeyID == Convert.ToInt32(id));
            var attachments = cardKey.Request.Attachments;
            return PartialView("_AttachmentsTable",attachments);
        }

jQuery:

$(function () {
    var table = $("#attachmentTable").DataTable();
    var path = MySettings.addAttachmentURL;
    $("#btnAddAttachment").click(function (event) {
        event.preventDefault();

        var formData = new FormData();
        var cardKeyID = $("#CardKeyID").val();

        var formData = $.each($("#files")[0].files, function (i, file) {
            formData.append('file-' + i, file);
        });
        console.log("formData = " + formData);
        $.ajax({
            type: "POST",
            url: path,
            contentType: false,
            processData:false,
            cache:false,
            data: {id:cardKeyID,files: formData },
            success: function (partialResult) {
                $("#tableData").html(partialResult);
                table = $("#attachmentTable").DataTable();
            },
            error: function (jqXHR, textStatus, errorThrown) {
                $("#message").html(JSON.stringify(jqXHR).toString());
                alert("AJAX error: " + textStatus + ' : ' + errorThrown);
            }
        });
    });
});

Razor View:

<fieldset>
            <legend>Add Attachments</legend>
        <div class="row">
            <div class="col-md-4">
                <div class="form-group">
                    @Html.Label("File Upload", new { @class = "control-label col-md-6" })
                    <div class="col-md-6">
                        <input type="file" id="files" name="files" multiple/><br />
                        <input type="submit" id="btnAddAttachment" value="Add Attachment"/>

                    </div>
                </div>
            </div>
            <div class="col-md-4">
                <div class="form-group">
                    @Html.Label("File Description", new { @class = "control-label col-md-6" })
                    <div class="col-md-6">
                        @Html.TextBox("FileDescription")
                    </div>
                </div>
            </div>
            <div class="col-md-4">
                <div class="form-group">
                    @Html.Label("Comment", new { @class = "control-label col-md-6" })
                    <div class="col-md-6">
                        @Html.TextBox("Comment")
                    </div>
                </div>
            </div>
        </div>
        <div id="tableData">
            @Html.Partial("_AttachmentsTable",Model.Request.Attachments)
        </div>
        </fieldset>
Meidi
  • 562
  • 1
  • 8
  • 28
  • You can't use the ModelBinder to retrieve uploaded files, you need to use `Request.Files`. See the question I marked as duplicate for more information. – Rory McCrossan Feb 10 '16 at 15:45
  • I checked Request object in debugging mode and Files property was empty. – Meidi Feb 10 '16 at 15:47
  • In which case, refer to the working jQuery example in the duplicate question for working code. – Rory McCrossan Feb 10 '16 at 15:48
  • I have tried those jQuery code, jQuery data is ok, but when pass ajax data into MVC action, parameters are all null. That's my problem. – Meidi Feb 10 '16 at 15:51
  • Ok, the duplicate I marked does have working code, but doesn't directly address your issues. I added an answer for you. – Rory McCrossan Feb 10 '16 at 15:57

1 Answers1

1

The issue is because you need to provide FormData directly to the data property of $.ajax, not within an object as it will be encoded. If you need to add data to the request, append it to the FormData object:

var formData = new FormData();
var cardKeyID = $("#CardKeyID").val();
var formData = $.each($("#files")[0].files, function (i, file) {
    formData.append('file-' + i, file);
});
formData.append('id', cardKeyID);

$.ajax({
    data: formData,
    // other properties here...
});

Then in the controller you need to retrieve the data directly from the Request:

[HttpPost]
public ActionResult AddAttachments()
{    
    var id = Request.Params["id"];
    foreach(var file in Request.Files) {
        //save files
    }

    var cardKey = db.CardKeys.Single(s => s.CardKeyID == Convert.ToInt32(id));
    var attachments = cardKey.Request.Attachments;
    return PartialView("_AttachmentsTable",attachments);
}

Note that if the form element contains all fields that you want to send in the request, you can avoid the $.each and having to append directly and simply use:

var formData = new FormData($('form')[0]);
Rory McCrossan
  • 331,213
  • 40
  • 305
  • 339
  • Hi Rory, var formData = new FormData($('form')[0]); does work. But when I use the example the code formData.append('id', cardKeyID); javascript complained: 0x800a01b6 - JavaScript runtime error: Object doesn't support property or method 'append'. That's very strange. – Meidi Feb 10 '16 at 16:30
  • That's odd. Which browser are you using? – Rory McCrossan Feb 10 '16 at 16:32