Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I handle HTML5 Multiple File uploads in ASP.NET MVC?

I found the following great thread with an explanation of how to do file uploads through AJAX/Jquery using the new HTML5 FormData API

Here's a slightly updated version of that code, with the newer JQuery 1.8+ syntax

$(':button').click(function(){
    var formData = new FormData($('form')[0]);
    $.ajax({
        url: '/Upload',  //my ASP.NET MVC method
        type: 'POST',
        // handle the progress report
        xhr: function() {  // Custom XMLHttpRequest
            var myXhr = $.ajaxSettings.xhr();
            if(myXhr.upload){ // Check if upload property exists
                myXhr.upload.addEventListener('progress',progressHandlingFunction,    false); // For handling the progress of the upload
            }
            return myXhr;
        },

        // Form data
        data: formData,

        //Options to tell jQuery not to process data or worry about content-type.
        cache: false,
        contentType: false,
        processData: false
    })
    .done(function(){
        alert("success");
    })
    .fail(function(){
        alert("error");
    });
});

function progressHandlingFunction(e){
    if(e.lengthComputable){
        $('progress').attr({value:e.loaded,max:e.total});
    }
}

and here's the form

<form enctype="multipart/form-data">
    <input name="file" type="file" />
    <input type="button" value="Upload" />
</form>
<progress></progress>

On the server side, we have something like this.

[HttpPost]
public string Upload(HttpPostedFileBase file)
{
    // do something with file
    return "you uploaded a file called " + file.FileName;
}

This works great. UNTIL you decide to use the "multiple" attribute on the file dialog, and send multiple files.

<form enctype="multipart/form-data">
    <input name="file" type="file" multiple="multiple" />
    <input type="button" value="Upload" />
</form>
<progress></progress>

You'll find various pages online suggesting the following solutions

public string Upload(IEnumerable<HttpPostedFileBase> files)
{
    foreach(var file in files)
         ...
}

Oops. Doesn't work

public string Upload(List<HttpPostedFileBase> files)
{
    foreach(var file in files)
         ...
}

Nope. Doesn't work.

public string Upload(IEnumerable files)
{
    foreach(var file in files)
         ...
}

Doesn't even compile

public string Upload(HttpPostedFileBase[] files)
{
    foreach(HttpPostedFileBase file in files)
         ...
}

Guess what? Doesn't work. Lets try dealing with Request.Files instead. Good old reliable Request.Files. Never fails.

public string Upload()
{
    foreach (HttpPostedFileBase uf in Request.Files)
         ...
}

Spoiler alert: It Doesn't work.

Aha. Got it! I'll iterate over the keys in Request.Files instead.

public string Upload()
{
    foreach(var key in Request.Files.AllKeys)
    {
        var file = Request.Files[key];
    }
}

Yet again, it doesn't work.

like image 396
roryok Avatar asked Feb 14 '14 16:02

roryok


1 Answers

What does work, is the following, from the blog of the always dependable and dynamically haired Rick Strahl

public string Upload()
{
    for (int i = 0; i < Request.Files.Count; i++)
    {
        var file = Request.Files[i];
    } 
}

The reason behind this is that the collection of files passed to Request.Files all have the same name, because they come from a singular file upload dialog.

the server side method is passed a single object containing the files, and for some reason Request.Files is the only way to get at it.

Hopefully I've saved someone a bit of headache by adding this in.

like image 160
roryok Avatar answered Oct 04 '22 12:10

roryok