Click here to Skip to main content
13,249,087 members (42,758 online)
Click here to Skip to main content
Add your own
alternative version

Stats

82.5K views
87 bookmarked
Posted 21 Aug 2015

Upload File Using Ajax And HTML5 in MVC

, 23 Dec 2016
Rate this:
Please Sign up or sign in to vote.
Simple native file Uploader with a progress bar without using flash player or any external file Upload plugins

Just download the project demo from here and enjoy. :)

Introduction

I was working in MVC application project and I wanted to upload file or multiple files with a progress bar info without using flash player or any upload file plugins. So in this post, we will explain a simple way to:

  • Upload single or multiple files with a progress bar info
  • Upload multiple files via Selecting files or Drag and Drop files

Background

HTML5 provides a standard way to interact with local files using File API specification, it gives us an opportunity to access files data or use client side validation to verify the size / type of uploaded files.

This specification includes several interfaces to access files:

  • A File interface which includes a read only info about file such as name, type and size in bytes.
  • A FileList interface which represents a list of individually selected files (list of file object) the user interface for selection can be invoked via <input type="file"> or drag & drop.

XMLHTTPRequest2 is one of the heroes of HTML5 universe, XHR2 are the same XMLHttpRequest but with many changes. It includes the following new features:

  • Uploading/downloading binary data.
  • Progress events during uploading. This event includes the following information:
    • Total: integer value that specifies total bytes being transferred.
    • Loaded: integer value that specifies bytes uploaded.
    • lengthComputable: Boolean value that checks if total Size of uploaded data is known.
  • Cross-origin requests

These features allow AJAX to work confidently with new technologies of HTML5 like the File API so by putting all these together, uploading file will be very easy and not a big deal without using flash player, external plugins or use normal html <form> and depend on server side to enable showing upload progress.

In this post, we’ll write a small application that is able to:

  • Upload single file and provide upload progress info?
  • Create a thumbnail preview of images before sending to server?
  • Upload multiple files via selecting multiple files or drag & drop files?

And we will check Browsers support for XHR2, File API, FormData and drag & drop.

Using the Code

How to Upload Single File and Providing Upload Progress Info?

All we have to do is create a simple View like below:

  • Form consists of input file element and button to submit the form.
  • Section for file info and a progress bar I used bootstrap progress bar:
<div id="FormContent">
           <form id="FormUpload"
           enctype="multipart/form-data" method="post">
               <span class="btn btn-success fileinput-button">
                   <i class="glyphicon glyphicon-plus"></i>
                   <span>Add files...</span>
                   <input type="file"
                   name="UploadedFile" id="UploadedFile" />
               </span>
               <button class="btn btn-primary start"
               type="button" id="Submit_btn">
                   <i class="glyphicon glyphicon-upload"></i>
                   <span>Start upload</span>
               </button>
                <button class="btn btn-warning cancel"
                type="button" id="Cancel_btn">
                   <i class="glyphicon glyphicon-ban-circle"></i>
                   <span>close</span>
               </button>
           </form>
           <div class="progress CustomProgress">
               <div id="FileProgress"
               class="progress-bar" role="progressbar"
       aria-valuenow="0" aria-valuemin="0"
       aria-valuemax="100" style="width: 0%;">
                   <span></span>
               </div>
           </div>
           <div class="InfoContainer">
               <div id="Imagecontainer"></div>
               <div id="FileName" class="info">
               </div>
               <div id="FileType" class="info">
               </div>
               <div id="FileSize" class="info">
               </div>
           </div>
       </div>

Then, we will add input file element’s onchange event and assign it to JS method called SingleFileSelected like in the below code snippet, so this method will be called every time user chooses/changes a file. In this method, we will select the input file element and access its files object of type FileList and select the first file (files[0]), this file of type file object gives us some read only information about file like file name, file type (mime type). We can use it to restrict some files and File size in bytes. This size we will convert it into MB and KB in our method so we can display them on the browser.

function singleFileSelected(evt) {
    //var selectedFile = evt.target.files can use this  or select input file element 
    //and access it's files object
    var selectedFile = ($("#UploadedFile"))[0].files[0];//FileControl.files[0];
    if (selectedFile) {
        var FileSize = 0;
        var imageType = /image.*/;
        if (selectedFile.size > 1048576) {
            FileSize = Math.round(selectedFile.size * 100 / 1048576) / 100 + " MB";
        }
        else if (selectedFile.size > 1024) {
            FileSize = Math.round(selectedFile.size * 100 / 1024) / 100 + " KB";
        }
        else {
            FileSize = selectedFile.size + " Bytes";
        }
        // here we will add the code of thumbnail preview of upload images
       
        $("#FileName").text("Name : " + selectedFile.name);
        $("#FileType").text("type : " + selectedFile.type);
        $("#FileSize").text("Size : " + FileSize);
    }
}

We can also use File reader object to read the uploaded file content into memory, the reader object has some events like onload, onError, four functions for reading data readAsBinaryString(), readAsText(), readAsArrayBuffer() and readAsDataURL() and the result property which represent file's contents. This property is only valid after the read operation is complete, and the format of the data depends on which of the methods was used to initiate the read operation.

We will not explain the File reader in detail, but we will use it in our SingleFileSelected method to preview images as thumbnail. See the below code snippet.

This code filters out images from uploaded files, then creates an object of Filereader and uses the onload event callback to create the image preview. This callback will get called after file reading operation is completed and the result is assigned to reader.result property, then we will call reader.readAsDataURL() that returns data as encoded DataURL.

if (selectedFile.type.match(imageType)) {
           var reader = new FileReader();
           reader.onload = function (e) {

               $("#Imagecontainer").empty();
               var dataURL = reader.result;
               var img = new Image()
               img.src = dataURL;
               img.className = "thumb";
               $("#Imagecontainer").append(img);
           };
           reader.readAsDataURL(selectedFile);
       }

So now, we will be able to load file name, type, size and Image preview like the figure below:

Now, we need to send the uploaded file to the server, so we will add onclick event and assign it to the JS method called uploadFile(). See the below code snippet:

function UploadFile() {
    //we can create form by passing the form to Constructor of formData object
    //or creating it manually using append function 
    //but please note file name should be same like the action Parameter
    //var dataString = new FormData();
    //dataString.append("UploadedFile", selectedFile);

    var form = $('#FormUpload')[0];
    var dataString = new FormData(form);
    $.ajax({
        url: '/Uploader/Upload',  //Server script to process data
        type: 'POST',
        xhr: function () {  // Custom XMLHttpRequest
            var myXhr = $.ajaxSettings.xhr();
            if (myXhr.upload) { // Check if upload property exists
                //myXhr.upload.onprogress = progressHandlingFunction
                myXhr.upload.addEventListener('progress', progressHandlingFunction, 
				false); // For handling the progress of the upload
            }
            return myXhr;
        },
        //Ajax events
        success: successHandler,
        error: errorHandler,
        complete:completeHandler,
        // Form data
        data: dataString,
        //Options to tell jQuery not to process data or worry about content-type.
        cache: false,
        contentType: false,
        processData: false
    });
}

In this method, we will send form using Form data object to serialize such file values we can create formdata manually by instantiating it, then appending fields to it by calling its append() method or retrieving a FormData object from an HTML form like we did in the above function by passing Form element to formdata constructor when creating it and also we can append more information to it. Then, we create JQuery AJAX with the right options:

  • XHR: Create custom XMLHTTPRequest check if upload Property Exists, then add a Progress event and assign it to JS method progressHandlingFunction that will handle progress of upload property .
  • processData: Set to false to tell jQuery not to process the data.
  • contentType: Set to false to tell jQuery not to set contentType.
  • Data: Set it to the formdata object.
    Other options are regular Ajax options and are pretty self-explanatory .

Let’s take a look at progressHandlingFunction. In this method, we check if total Size of uploaded data is known via e.lengthComputable, then we use e.loaded(value of uploaded bytes) and e.total(value of total bytes being transferred) to compute the percentage of uploaded data.

function progressHandlingFunction(e) {
    if (e.lengthComputable) {
        var percentComplete = Math.round(e.loaded * 100 / e.total);
        $("#FileProgress").css("width", 
        percentComplete + '%').attr('aria-valuenow', percentComplete);
        $('#FileProgress span').text(percentComplete + "%");
    }
    else {
        $('#FileProgress span').text('unable to compute');
    }
}

So now, we are able to send data to server and provide a progress. Let's take a look at the server side code in the below snippet which is a very simple action called upload in controller called uploader.

In this action, we received the file in HttpPostedfileBase object. This object contains information about the uploaded file like Filename property, Contenttype property and inputStream property that contains file content with this information we can validate file on server save file.

[HttpPost]

        public JsonResult Upload(HttpPostedFileBase uploadedFile)
        {
            if (uploadedFile != null && uploadedFile.ContentLength > 0)
            {
                byte[] FileByteArray = new byte[uploadedFile.ContentLength];
                uploadedFile.InputStream.Read(FileByteArray, 0, uploadedFile.ContentLength);
                Attachment newAttchment = new Attachment();
                newAttchment.FileName = uploadedFile.FileName;
                newAttchment.FileType = uploadedFile.ContentType;
                newAttchment.FileContent = FileByteArray;
                OperationResult operationResult = attachmentManager.SaveAttachment(newAttchment);
                if (operationResult.Success)
                {
                    string HTMLString = CaptureHelper.RenderViewToString
                    ("_AttachmentItem", newAttchment, this.ControllerContext);
                    return Json(new
                    {
                        statusCode = 200,
                        status = operationResult.Message,
                        NewRow = HTMLString
                    }, JsonRequestBehavior.AllowGet);

                }
                else
                {
                    return Json(new
                    {
                        statusCode = 400,
                        status = operationResult.Message,
                        file = uploadedFile.FileName
                    }, JsonRequestBehavior.AllowGet);

                }
            }
            return Json(new
            {
                statusCode = 400,
                status = "Bad Request! Upload Failed",
                file = string.Empty
            }, JsonRequestBehavior.AllowGet);
        }

Upload Multiple Files Via Selecting Files or Drag & Drop Files?

In this section, we will implement the same uploader, but with some new features:

  • Allow selecting multiple files
  • Drag and drop files

We will create the same view like the one in single uploader section, but we need to add a few things:

  • Adding multiple attribute to the input file element that allow selecting multiple files
  • Adding section for drag and drop files like in the below code snippet
<div id="drop_zone">Drop images Here</div>

Then, we will add onchange event and assign it to JS method called MultiplefileSelected like we did before with SingleFileSelected method, but here we will play with all the files in the files list object and allow Drag and Drop files. See the code snippet.

In this method, we assigned the selected/dragged files to a global variable called selectedFiles, then for each file in selectedfiles, we will read the file using Read method in our DataURLreader object. I made this object for making the code a little bit clearer and readable instead of creating file reader for each file in the same method. After reading file, we can render the image information or any error. Let’s take a look at DataURLreader object.

function MultiplefileSelected(evt) {
    evt.stopPropagation();
    evt.preventDefault();
    $('#drop_zone').removeClass('hover');
    selectedFiles = evt.target.files || evt.dataTransfer.files;
    if (selectedFiles) {
        $('#Files').empty();
        for (var i = 0; i < selectedFiles.length; i++) {
            DataURLFileReader.read(selectedFiles[i], function (err, fileInfo) {
                if (err != null) {
                    var RowInfo = '<div id="File_' + i + '" 
                    class="info"><div class="InfoContainer">' +
                                   '<div class="Error">' + err + '</div>' +
                                  '<div data-name="FileName" 
                                  class="info">' + fileInfo.name + '</div>' +
                                  '<div data-type="FileType" 
                                  class="info">' + fileInfo.type + '</div>' +
                                  '<div data-size="FileSize" 
                                  class="info">' + fileInfo.size() + 
                                  '</div></div><hr/></div>';
                    $('#Files').append(RowInfo);
                }
                else {
                    var image = '<img src="' + fileInfo.fileContent + 
                    '" class="thumb" title="' + 
                    fileInfo.name + '" />';
                    var RowInfo = '<div id="File_' + i + '" 
                    class="info"><div class="InfoContainer">' +
                                  '<div data_img="Imagecontainer">' + 
                                  image + '</div>' +
                                  '<div data-name="FileName" 
                                  class="info">' + fileInfo.name + '</div>' +
                                  '<div data-type="FileType" 
                                  class="info">' + fileInfo.type + '</div>' +
                                  '<div data-size="FileSize" 
                                  class="info">' + fileInfo.size() + 
                                  '</div></div><hr/></div>';
                    $('#Files').append(RowInfo);
                }
            });
        }
    }
}

The DataURLFileReader object contains read method that takes a file and callback method as parameters, at the first of the method, we create a new fileReader and handle its onload and onerror callback methods and at the end of function, we call readAsDataURL method to read the file, we create an object called fileInfo that will contain all file information and file content after it is loaded.

var DataURLFileReader = {
    read: function (file, callback) {
        var reader = new FileReader();
        var fileInfo = {
            name: file.name,
            type: file.type,
            fileContent: null,
            size: function () {
                var FileSize = 0;
                if (file.size > 1048576) {
                    FileSize = Math.round(file.size * 100 / 1048576) / 100 + " MB";
                }
                else if (file.size > 1024) {
                    FileSize = Math.round(file.size * 100 / 1024) / 100 + " KB";
                }
                else {
                    FileSize = file.size + " bytes";
                }
                return FileSize;
            }
        };
        if (!file.type.match('image.*')) {
            callback("file type not allowed", fileInfo);
            return;
        }
        reader.onload = function () {
            fileInfo.fileContent = reader.result;
            callback(null, fileInfo);
        };
        reader.onerror = function () {
            callback(reader.error, fileInfo);
        };
        reader.readAsDataURL(file);
    }
};

Using Drag and Drop for Selecting

Another technique for loading files is native drag and drop from local machine to browser and we will not use another plugin. Now all of the major browsers actually have a native support for Drag & Drop.

To start working with Drag and Drop, we need to add dragover and drop events on drop_zone element.

var dropZone = document.getElementById('drop_zone');
    dropZone.addEventListener('dragover', handleDragOver, false);
    dropZone.addEventListener('drop', MultiplefileSelected, false);
    dropZone.addEventListener('dragenter', dragenterHandler, false);
    dropZone.addEventListener('dragleave', dragleaveHandler, false);

The dragover event will fire when the file dragged over the drop target, in the handler of this method, we just prevent defaults of the browser and change the dropEffect property of the datatransfer object to copy, see code below:

function handleDragOver(evt) {
    evt.preventDefault();
    evt.dataTransfer.effectAllowed = 'copy';
    evt.dataTransfer.dropEffect = 'copy';
}

Then we will add the drop event and assign it with our MultiplefileSelected method to handle dropped files. It is good practice to add dragenter and dragLeave events. With these events, you can change the styles of Drop zone by adding CSS class to Dropzone element when Dragenter event fired and remove this class when dragleave event fired.

Now, we are ready to hit the start upload button to send files to the server. We just add onclick event that will call UploadMultipleFiles method when button is clicked.

This method is similar to our previous method Uploadfile except one thing we create the formdata object manually to validate/prevent sending non image files.

function UploadMultipleFiles() {

    // here we will create FormData manually to prevent sending mon image files
    var dataString = new FormData();
    //var files = document.getElementById("UploadedFiles").files;
    for (var i = 0; i < selectedFiles.length; i++) {
        if (!selectedFiles[i].type.match('image.*')) {
            continue;
        }
       }
// AJAX Request code here
}

Now the last thing we have to do is the server side code and it also similar to our previous single upload server side code. The only thing is we will receive a list of files, so our action signature should be like that:

public JsonResult UplodMultiple(HttpPostedFileBase[] uploadedFiles)

And make sure that HttpPostedFileBase array name is the same as the object name in the append method of formdata object. By this way, the MVC can map the files array.

public JsonResult UplodMultiple(HttpPostedFileBase[] uploadedFiles)
dataString.append("uploadedFiles", selectedFiles[i]);

Upload Big Size Files

To allow uploading big size files. if you use IIS 7 or greater. you should modify the web.config file and add the two sections below:

<system.webServer>
       <security>
                <requestFiltering>
                           <requestLimits maxAllowedContentLength="2147483648" />
                </requestFiltering>
       </security>
</system.webServer>

<httpRuntime targetFramework="4.5"  maxRequestLength="2097152"/>

This will allow you to upload file up to 2 giga.

Note: maxAllowedContentLength is measured in bytes while maxRequestLength is measured in kilobytes which is why the values are different (both are equivalent to 2 GB).

Browsers Support

License

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

Share

About the Author

Muhammad Magdi
Software Developer
Egypt Egypt
I’m a senior software engineer with +4 years of experience in .net technology stack. I deeply love what I do as a software engineer. I have a great passion learning new frameworks, tools, techniques in this area and sharing knowledge with community.

You may also be interested in...

Pro
Pro

Comments and Discussions

 
Questionsource code Pin
dariya teset5-Jul-17 7:05
memberdariya teset5-Jul-17 7:05 
AnswerRe: source code Pin
Muhammad Magdi8-Jul-17 9:06
memberMuhammad Magdi8-Jul-17 9:06 
GeneralRe: source code Pin
SwapnilGovalkar25-Oct-17 4:43
memberSwapnilGovalkar25-Oct-17 4:43 
QuestionAdding multiple files by opening browse multiple times Pin
Denis Lazendić29-May-17 5:25
memberDenis Lazendić29-May-17 5:25 
QuestionBut what if you do not want to use jQuery? Pin
Member 1295130015-Jan-17 15:56
memberMember 1295130015-Jan-17 15:56 
QuestionGood Pin
yogeshcs200311-Jan-17 19:56
memberyogeshcs200311-Jan-17 19:56 
AnswerRe: Good Pin
Muhammad Magdi12-Jan-17 11:15
memberMuhammad Magdi12-Jan-17 11:15 
Question"AttachmentContext" connectionString Pin
Member 1030098228-Dec-16 16:14
professionalMember 1030098228-Dec-16 16:14 
AnswerRe: "AttachmentContext" connectionString Pin
Muhammad Magdi3-Jan-17 10:39
memberMuhammad Magdi3-Jan-17 10:39 
GeneralMy vote of 4 Pin
Omar Nasri22-Dec-15 6:06
memberOmar Nasri22-Dec-15 6:06 
PraiseVote 5 Pin
guillermo26256-Nov-15 7:39
memberguillermo26256-Nov-15 7:39 
QuestionNeed information about browsers support Pin
V.J. Kumar10-Sep-15 2:58
memberV.J. Kumar10-Sep-15 2:58 
AnswerRe: Need information about browsers support Pin
sqwertik13-Sep-15 11:35
membersqwertik13-Sep-15 11:35 
GeneralRe: Need information about browsers support Pin
Muhammad Magdi15-Sep-15 4:41
memberMuhammad Magdi15-Sep-15 4:41 
QuestionSuperb article Pin
V.J. Kumar10-Sep-15 1:55
memberV.J. Kumar10-Sep-15 1:55 
GeneralMy vote of 5 Pin
3Asool31-Aug-15 5:17
member3Asool31-Aug-15 5:17 
GeneralMy vote of 5 Pin
Humayun Kabir Mamun23-Aug-15 21:54
memberHumayun Kabir Mamun23-Aug-15 21:54 
SuggestionFormat issues.. Pin
Afzaal Ahmad Zeeshan22-Aug-15 2:52
professionalAfzaal Ahmad Zeeshan22-Aug-15 2:52 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.171114.1 | Last Updated 23 Dec 2016
Article Copyright 2015 by Muhammad Magdi
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid