Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Multiple File Upload in MVC

4.97/5 (13 votes)
7 Aug 2014CPOL2 min read 128.4K   6.3K  
Multiple file upload in MVC

Introduction

I had a requirement in MVC 4 application - A multi file uploader control that has options:

  • Select multiple files at a time
  • Select from different folder
  • List all selected files
  • Remove files from the list (Filtering)
  • When submitting - Save only filtered one
  • Get page model also in the Action
  • Save attachments after saving the model saving or some business logic

Issues I Faced

I was getting the partial needs - Uploader control, but Model received is empty - at Action in our controller.
When Model is filled, the filtered list is incorrect (deleted files also appeared).

How I Solved It

I created a JS script that will make the <input type="file" /> control get recreated and preserve the HttpPostedFileBase files + removed/filtered files in a Hidden field - This maintains the filtration in deleting selected files also.

When user puts some files in the fileupload control, "onchange" event, it will hide the control and create a similar control and place there and allow users to select again. The selected files are taken and maintained in a Hidden field appending its names. With these names - we can filter the posted files list (List<httppostedfilebase>) in our action. So while passing Model - keep this hidden field value.

Using the Code

Please find the source code attached here for the full demo.

I will explain how it works. See the code snippet below.

Given below is the snippet of HTML that has a fileUploader and a button that submits to our server action.

HTML
@using (Html.BeginForm("UploadAction", "Home", 
FormMethod.Post, new { enctype = "multipart/form-data", id = "frmID" }))
{
    @Html.HiddenFor(i => i.FilesToBeUploaded)

    <div class="labelstyle">
        <label>Files</label>
    </div>

    <div id="uploaders">
        <input type="file" id="fileToUpload" 
        name="fileUpload" multiple="multiple" style="float: left;" />
        <br />
        <span id="spnFile" style="float: left; color: #FF0000"></span>
        @Html.ValidationMessage("File")
        @Html.Hidden("hdnFileUpload")
    </div>
    <br />
 <div class="col-lg-6">
        <button class="btn btn-primary" id="btnAddIssue" type="submit">Submit</button>
    </div>
    <br />
    <div class="control-section" style="padding: 0px;">
        <div id="selectedFiles"></div>
    </div>
}

This portion is the script that is responsible for creation of the multi file upload and hiding the previous uploader + storing the filtered attachments to a hidden field.

HTML
<script src="~/Scripts/jquery-1.7.1.js"></script>

<script>
    var nowTemp = new Date();
    var now = new Date(nowTemp.getFullYear(), nowTemp.getMonth(), nowTemp.getDate(), 0, 0, 0, 0);
    var files;
    var storedFiles = [];
    var upc = 0;   

    $(function () {

        $(":file").attr('title', '  ');
        var $loading = $('#loadingDiv').hide();

        $("input[id^='fileToUpload']").change(function (e) {
            doReCreate(e);
        });

        selDiv = $("#selectedFiles");
    });

    function doReCreate(e) {
        alert('a');
        upc = upc + 1;
        handleFileSelect(e);

        $("input[id^='fileToUpload']").hide();

        $('<input>').attr({
            type: 'file',
            multiple: 'multiple',
            id: 'fileToUpload' + upc,
            class: 'fUpload',
            name: 'fileUpload',
            style: 'float: left',
            title: '  ',
            onchange: "doReCreate(event)"

        }).appendTo('#uploaders');
    }

    function handleFileSelect(e) {

        //selDiv.innerHTML = ""; storedFiles = []; 
        selDiv = document.querySelector("#selectedFiles");

        if (!e.target.files) return;

        //selDiv.innerHTML = "";
        files = e.target.files;

        for (var i = 0; i < files.length; i++) {
            //if (i == 0) { selDiv.innerHTML = ""; storedFiles = []; }
            var f = files[i];
            selDiv.innerHTML += "<div>" + f.name + 
            "<a onclick='removeAtt(this)'> X </a></div>";
            storedFiles.push(f.name);
        }
        $('#@Html.IdFor(i => i.FilesToBeUploaded)').val(storedFiles);
    }

    function removeAtt(t) {
        var serEle = $(t).parent().text().slice(0, -3);
        var index = storedFiles.indexOf(serEle);
        if (index !== -1) {
            storedFiles.splice(index, 1);
        }
        $(t).parent().remove();

        $('#@Html.IdFor(i => i.FilesToBeUploaded)').val(storedFiles);

    }
</script>

And find the server code, where it receives the model + hidden element and the full attachments that we need.

C#
[HttpPost]
       public ActionResult UploadAction(AnyModel model, List<HttpPostedFileBase> fileUpload)
       {
           // Your Code - / Save Model Details to DB

           // Handling Attachments -
           foreach (HttpPostedFileBase item in fileUpload)
           {
               if (Array.Exists(model.FilesToBeUploaded.Split(','), s => s.Equals(item.FileName)))
               {
                   //Save or do your action -  Each Attachment ( HttpPostedFileBase item )
               }
           }
           return View("Index");
       }

Also, if style "X" to remove selected files, update your script code accordingly.

Points of Interest

This is a simple one and a tricky approach - Recommended for small file(s) size - But can be any number of files .

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)