HTML5 Drag and Drop file upload with preview using jQuery and MVC 4

With the advent of HTML5 and its wide adoption by all major browsers, web developers now have a new arsenal in their hands for implementing powerful file upload functionality on their website. In this tutorial we will see how to implement this using the FileDrop jQuery plugin and an MVC controller to receive and store the uploaded files in a repository on the server.

First create a new MVC 4 website in Visual Studio. An existing website can also be used.  Create a new blank view and name it “UploadFiles”. You should be able to navigate to the page using a url similar to this http://localhost:62615/Home/UploadFiles/  Ignore the first part as it will be different on your machine. If you wish, you can add a navigation link on your _layout.cshtml page to make this page more accessible. Next, go to your HomeController and create the following method:

public ViewResult UploadFiles()
{
      return View();
}

This is the GET method that will allow us to navigate to the UploadFiles view when using the url i mentioned earlier. Now open your View file and edit it in Visual studio. Ensure that the page has a reference to the jquery library either through the _Layout.cshtml page or by directly adding a reference here. The html code required for the plugin is minimal. First, you need to add the libraries that will provide the FileDrop functionality:

<script src="~/Scripts/jquery.filedrop.js"></script>
<script src="~/Scripts/jquery.filedrop.support.js"></script>
<link href="~/Content/styles.css" rel="stylesheet" />

<h2>File Drag &amp; Drop Upload Demo</h2>

<div id='dropbox'>
    <span class="message">Drop images here to upload. <br /><i>(they will only be visible to you)</i></span>
</div>

Next you need to add a new javascript file under Scripts that will hold the code for the custom FileDrop actions. I named mine jquery.filedrop.support.js.  Copy and paste the following code to your javascript file:

$(function () {
    var dropbox = $('#dropbox'),
		message = $('.message', dropbox);

    dropbox.filedrop({
        url: 'http://localhost:62615/Home/UploadFiles',
        paramname: 'files',
        maxFiles: 10,
        maxFileSize: 4,
        allowedfiletypes: ['image/jpeg', 'image/png', 'image/gif'],
        uploadFinished: function (i, file, response) {
            $.data(file).addClass('done');
            // response is the JSON object that controller returns
        },

        error: function (err, file) {
            switch (err) {
                case 'BrowserNotSupported':
                    showMessage('Your browser does not support HTML5 file uploads!');
                    break;
                case 'TooManyFiles':
                    alert('Too many files! Please select 10 at most! (configurable)');
                    break;
                case 'FileTooLarge':
                    alert(file.name + ' is too large! Please upload files up to 4mb (configurable).');
                    break;
                case 'FileTypeNotAllowed':
                    alert(file.name + ' is not supported. You can only upload files with .gif .png and .jpg extension');
                    break;
                default:
                    break;
            }
        },

        uploadStarted: function (i, file, len) {
            createImage(file);
        },

        progressUpdated: function (i, file, progress) {
            $.data(file).find('.progress').width(progress);
        }

    });

    var template = '<div class="preview">' +
			'<span class="imageHolder">' +
			    '<img />' +
			    '<span class="uploaded"></span>' +
			'</span>' +
			'<div class="progressHolder">' +
				'<div class="progress"></div>' +
			'</div>' +
                      '<div class="remove">Remove</div>' +
		     '</div>';

    function createImage(file) {
        var preview = $(template), image = $('img', preview);

        var reader = new FileReader();

        image.width = 100;
        image.height = 100;

        reader.onload = function (e) {

            // e.target.result holds the DataURL which
            // can be used as a source of the image:

            image.attr('src', e.target.result);
        };

        // Reading the file as a DataURL. When finished,
        // this will trigger the onload function above:
        reader.readAsDataURL(file);

        message.hide();
        preview.appendTo(dropbox);

        // Associating a preview container
        // with the file, using jQuery's $.data():

        $.data(file, preview);
    }

    function showMessage(msg) {
        message.html(msg);
    }
});

Alternatively, you can add the code inside the view using the tags. I like to keep my pages clean hence the separate javascript file. A breakdown of properties that you may want to change is listed below:

  • url: this should point to the controller action that will handle the file uploads
  • paramname: this should be the controller action parameter. The variable name in javascript and your cs file need to match
  • maxFile: maximum number of files allowed to for upload
  • maxFileSize: the maximum size of each file in MBs
  • allowedFileTypes: if left blank then all file types are allowed

The createImage(file) method is used to create a thumbnail for the preview and nothing more.

Finally, go back to your HomeController and add the method that will accept the uploaded files and store them in the filesystem. At this stage, you may choose to perform your image manipulation and change the storage to include a database. For this scenario we will use the file system as our image repository.

[HttpPost]
public ActionResult UploadFiles(IEnumerable<HttpPostedFileBase> files)
{
            foreach (HttpPostedFileBase file in files)
            {
                string filePath = Path.Combine(TempPath, file.FileName);
                File.WriteAllBytes(filePath, this.ReadData(file.InputStream));
            }
            return this.Json("All files have been successfully stored.");
}

private byte[] ReadData(Stream stream)
{
            byte[] buffer = new byte[16 * 1024];
            using (MemoryStream ms = new MemoryStream())
            {
                   int read;
                   while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
                   {
                        ms.Write(buffer, 0, read);
                    }
                    return ms.ToArray();
            }
}

The code above will read the list of files and store them one at a time in the TempPath location. TempPath can be any existing directory in your system.

Note that the code will overwrite images with the same name and we never check for valid filenames or if the directory exists .

Downloads: HTML5 DragAndDrop FileUpload

Happy coding....


  • Share this post on