11

I'm migrating a project from ASP.NET RC1 to ASP.NET Core 1.0.

I have a view that allows users to upload one of more files, which I post using Jquery Ajax. I also serialize and post some settings within the same post.

The following all worked in RC1 (and pre-asp.net core):

Js:

    $('#submit').click(function () {      
        var postData = $('#fields :input').serializeArray();
        var fileSelect = document.getElementById('file-select');
        var files = fileSelect.files;

        var data = new FormData();
        for (var i = 0; i < files.length; i++) {
            data.append('file' + i, files[i]);
        }
        $.each(postData, function (key, input) {
            data.append(input.name, input.value);
        });
        var url = '/ajax/uploadfile';
        $.ajax({
            url: url,
            type: "POST",
            contentType: false,
            processData: false,
            cache: false,
            data: data,
            success: function (result) {
                alert('success');                   
            },
            error: function () {
                alert('error'); 
            }
        });
    });

Controller:

  public IActionResult UploadFile(UploadFileModel model)
    {
        var result = new JsonResultData();
        try
        {
            if (Request.Form.Files.Count > 0)
            {
                IFormFile file = Request.Form.Files[0];
                //etc
             }
        }
     }

So the above does not work anymore, no file uploaded and no model bound. I managed to fix half the issues so now I can get the model to bind with the following code. However, the controller will still give me an exception on the Request.Files. I added the 'headers' property, and I used serializeObject (custom method). In the controller I added FromBody.

Js:

 $('#submit').click(function () {      
        var postData = $('#fields :input').serializeArray();
        var fileSelect = document.getElementById('file-select');
        var files = fileSelect.files;

        var data = new FormData();
        for (var i = 0; i < files.length; i++) {
            data.append('file' + i, files[i]);
        }
        $.each(postData, function (key, input) {
            data.append(input.name, input.value);
        });
        var url = '/ajax/uploadfile';
        $.ajax({
            url: url,
            type: "POST",
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json'
            },
            processData: false,
            cache: false,
            data: serializeAndStingifyArray(data),
            success: function (result) {
                alert('success');                   
            },
            error: function () {
                alert('error'); 
            }
        });
    });

    function serializeAndStingifyArray(array) {
    var o = {};
    var a = array;
    $.each(a, function () {
        if (o[this.name] !== undefined) {
            if (!o[this.name].push) {
                o[this.name] = [o[this.name]];
            }
            o[this.name].push(this.value || '');
        } else {
            o[this.name] = this.value || '';
        }
    });
    return JSON.stringify(o);
};

Controller:

    [HttpPost]
    public IActionResult UploadFile([FromBody]UploadFileModel model)
    {
        var result = new JsonResultData();
        try
        {
            if (Request.Form.Files.Count > 0)
            {
                IFormFile file = Request.Form.Files[0];
                //etc
             }
         }
       }

html:

    <div id="file-list">
    <input type="file" name="file" class="file-select" accept="application/pdf,application">
    <input type="file" name="file" class="file-select"           accept="application/pdf,application" />
    </div>
Amirhossein Mehrvarzi
  • 18,024
  • 7
  • 45
  • 70
user2713516
  • 2,983
  • 5
  • 23
  • 49
  • 2
    Can you share how your `UploadFileModel` model looks like? and also how your multipart request looks like? (from browser dev tools) – Kiran Jul 20 '16 at 18:39
  • In this case the uploadfile model only has one property which is the language setting (hidden field in the view). This binds fine, the problem seems to lie within the file sending, I added the html, I'm not really sure what info you're looking for regarding the request. – user2713516 Jul 22 '16 at 09:54
  • Your controller doesn't look quite correct, look [HERE](http://stackoverflow.com/questions/5088450/how-to-retreive-form-values-from-httppost-dictionary-or) for a similar question. – Frank Jul 28 '16 at 20:24
  • @Frank what part is not correct? keep in mind that this is asp.net core not mvc 3 as the question you linked. – user2713516 Aug 01 '16 at 06:45
  • What is the exception you get when accessing `Request.Files`? – Dean Ward Aug 01 '16 at 08:30
  • @user2713516 Does the FormData object "data" contain any key/value pairs when you make the ajax call? You could also get the file data by: var i = 0; $(".file-select").each(function () { data.append('file' + i, $(this).val()); i++; }); – Frank Aug 02 '16 at 14:00
  • Why you don't use `HttpPostedFileBase` as `Collection`? – Amirhossein Mehrvarzi Aug 02 '16 at 16:57
  • @user2713516 do you mind checking my answer? – Gerardo Grignoli Aug 08 '16 at 18:37
  • I got it working using the selected answer below, please see my comment there. Thanks everyone for the input – user2713516 Aug 21 '16 at 15:16

1 Answers1

15

I started from this article which has some code that is almost the same as yours Upload Files In ASP.NET Core 1.0 (see Ajax case).

That worked for me fine on 1.0.0, so I implemented your changes and what I saw is that it failed to send the files in the request (client side issue).

This is how the payload should look like when working ok using F12 in chrome: (not sure why the file contents are hidden by chrome).

payload

A little debugging and you are passing wrong data to data.append

The fix is in this line

        $(".file-select").each(function () { data.append($(this).val(), $(this).get(0).files[0]); i++; })

Full code:

$(document).ready(function () {
    $("#submit").click(function (evt) {

        var data = new FormData();
        i = 0;

        $(".file-select").each(function () { data.append($(this).val(), $(this).get(0).files[0]); i++; })

        var postData = $('#fields :input');
        $.each(postData, function (key, input) {
            data.append(input.name, input.value);
        });

        $.ajax({
            type: "POST",
            url: "/ajax/uploadfile",     // <--- Double check this url.
            contentType: false,
            processData: false,
            data: data,
            success: function (message) {
                alert(message);
            },
            error: function () {
                alert("There was error uploading files!");
            }
        });
    });
});

No need to use [FromBody] or serializeArray()

    [HttpPost]
    public IActionResult UploadFilesAjax(MyViewModel xxx )
    {

This is my html, just in case:

<form method="post" enctype="multipart/form-data">

<div id="file-list">
    <input type="file" name="file" class="file-select" accept="application/pdf,application">
    <input type="file" name="file" class="file-select" accept="application/pdf,application" />
</div>
<div id="fields">
    <input type="text" name="Email" />
</div>

<input type="button"
        id="submit"
        value="Upload Selected Files" />
</form>
Gerardo Grignoli
  • 14,058
  • 7
  • 57
  • 68
  • 1
    Yeah you're correct about passing wrong data to the FormData object "data", I noticed that as well. – Frank Aug 05 '16 at 14:04
  • Sorry for the late reply as I wasn't able to look at that particular project for a while. I tried your solution and I managed to get it to work using your code as a guideline. I did still need to apply serializeArray() on the $('#fields :input') selector in order to serialize the correct fields (e.g. the correct checkbox values and radiobtn values.) Anyway, you saved my day so thank you :) – user2713516 Aug 21 '16 at 15:15
  • Great post. What about JsonResult? – JoshYates1980 Jan 29 '17 at 03:16