January 2018 : Instructor-led Online Course in ASP.NET Core 2.0. Conducted by Bipin Joshi. Read more...
Registration for January 2018 batch of ASP.NET Core 2.0 instructor-led online course has already started. Conducted by Bipin Joshi. Register today ! Click here for more details.

Drag and Drop File Upload in ASP.NET Core

In one of my earlier articles I discussed how to upload files using full page post-back as well as Ajax post-back. Usually developers use the file input field to select one or more files to be uploaded from the client machine. However, you can also HTML5 drag-n-drop to make it easy for the end user to pick files directly from Windows explorer. To that end this article shows how to implement just that.

Before you begin developing this example, let's see the expected behavior of the application. Have a look at the following figure that shows a region of the page (let's call it file basket) ready accept the files.

You can open Windows explorer, select one or more files that you want to upload, and then drag-n-drop them onto this file basket.

Once you drag-n-drop the files, they will be listed in the file basket and immediately the file upload operation will begin. A progress indicator displays that the file upload operation is in progress.

Once the files are successfully uploaded to the server, the file basket shows the number of files uploaded along with the total byte count.

Ok. Now that you understood how the application works, let's write some code.

Create a new ASP.NET Core MVC web application. Add HomeController and Index view as you normally do. We need jQuery library because the file upload will happen through jQuery Ajax. So, create Scripts folder under wwwroot folder and place the jQuery library files there. Also create Images folder and place a progress indicator image there (you can use any image or animated GIF of your choice). Finally, create UploadedFiles folder to store the files uploaded on the server. This is how your Solution Explorer should looks like at this stage :

Open the Index view and write the <form> markup as shown below :

<form method="post" enctype="multipart/form-data">
  <div id="fileBasket" class="filebasket">
     Drag-n-Drop files here.
  </div>
  <br />
  <img id="progress" src="~/Images/Progress.gif" />
</form>

Notice that the form contains the file basket <div> element and the progress indicator <img> element.

Next, add a script reference to the jQuery library in the <head> section. Also add another empty <script> block below the script reference.

Now it's time to write some jQuery code. First of all let's write code that implements drag-n-drop. In order to implement drag-n-drop you need to handle three events - dragenter, dragover, and drop. The skeleton of these event handlers is shown below :

$(document).ready(function () {

    $("#progress").hide();

    $("#fileBasket").on("dragenter", function (evt) {
        evt.preventDefault();
        evt.stopPropagation();
    });

    $("#fileBasket").on("dragover", function (evt) {
        evt.preventDefault();
        evt.stopPropagation();
    });

    $("#fileBasket").on("drop", function (evt) {
        evt.preventDefault();
        evt.stopPropagation();
        ...
        ...
     });
});

The code basically wires the previously mentioned on the fileBasket element. In all the event handlers we call preventDefault() and stopPropagation() to prevent the default action and to stop the bubbling-up of the event.

Also notice that the ready() function hides the progress indicator image so that initially the progress indicator is not displayed.

Now let's discuss the drop event handler where the main code is to be placed.

$("#fileBasket").on("drop", function (evt) {
    evt.preventDefault();
    evt.stopPropagation();
    var files = evt.originalEvent.dataTransfer.files;
    var fileNames = "";
    if (files.length > 0) {
        fileNames += "Uploading <br/>"
        for (var i = 0; i < files.length; i++) {
            fileNames += files[i].name + "<br />";
        }
    }
    $("#fileBasket").html(fileNames)

    var data = new FormData();
    for (var i = 0; i < files.length; i++) {
        data.append(files[i].name, files[i]);
    }
    $.ajax({
        type: "POST",
        url: "/home/UploadFiles",
        contentType: false,
        processData: false,
        data: data,
        success: function (message) {
            $("#fileBasket").html(message);
        },
        error: function () {
            $("#fileBasket").html
("There was error uploading files!");
        },
        beforeSend: function () {
            $("#progress").show();
        },
        complete: function () {
            $("#progress").hide();
        }
    });
});

The code shown above grabs the files drag-n-dropped by the user using the evt.originalEvent.dataTransfer.files property. It then iterates through the files array and gets the file names using the name property. The list of names is then filled into the fileBasket element.

Then the code creates a new FormData object. The FormData is a programmatic representation of the form values to be submitted to the server. Another for loop iterates through the files array again and adds all the selected files to the FormData object. This is done using the append() method of FormData.

Then an Ajax POST request is made to the UploadFiles() action of the HomeController. Notice that the contentType and procesData properties are set to false since we are sending files through the FormData. The data property is set to the FormData object. The success handler shows a success message (more on that soon). The error handler displays an error message. The beforeSend and complete handlers show and hide the progress indicator image respectively.

The UploadFiles() action of the HomeController used by the above Ajax call is shown below :

[HttpPost]
public IActionResult UploadFiles()
{
    long size = 0;
    var files = Request.Form.Files;
            
    foreach (var file in files)
    {
        string filename = hostingEnv.WebRootPath 
+ $@"\uploadedfiles\{file.FileName}";
        size += file.Length;
        using (FileStream fs = 
System.IO.File.Create(filename))
        {
            file.CopyTo(fs);
            fs.Flush();
        }
    }
    string message = $"{files.Count} file(s) / 
{size} bytes uploaded successfully!";
    return Json(message);
}

The UploadFiles() method grabs the files accompanying the request using the Request.Form.Files collection. A foreach loop iterates through the files collection. Each entry from the files collection is an IFormFile object. We need to compute the server side path where the file is to be stored. This is done using the WebRootPath property of IHostingEnvironment object. You need to inject the IHostingEnvironment object into the controller (discussed later) to access it in the UploadFiles() method. The FileName property gives the client side file name and we append it to the UploadedFiles folder. The Length property gives the size of the file and we add it to the size variable.

Then the code creates a new FileStream and copies the files's content into it. This way the uploaded file gets saved to a file pointed by the FileStream object.

Once all the files are uploaded, a success message is formed that informs the user about the number of files uploaded and their total size. The success message is returned to the client using the Json() method.

The above code uses IHostingEnvironment injected using constructor injection. This is how that piece of code looks like :

public class HomeController : Controller
{
    private IHostingEnvironment hostingEnv;

    public HomeController(IHostingEnvironment env)
    {
        this.hostingEnv = env;
    }
}

The code declares IHostingEnvironment variable - hostingEnv - in the HomeController. The constructor receives an IHostingEnvironment object through the DI framework and stores it in the hostingEnv variable.

This completes the code. You can now run the application and test the working by dragging and dropping a few files from the Windows explorer. If all goes well you will see those files inside the UploadedFiles folder.

That's it for now! Keep coding !!


Bipin Joshi is a software consultant, an author and a yoga mentor having 22+ years of experience in software development. He also conducts online courses in ASP.NET MVC / Core and Design Patterns. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced the Yoga way of life he also teaches Ajapa Yoga to interested individuals. To know more about him click here.

Get connected : Twitter  Facebook  Google+  LinkedIn

Posted On : 20 November 2017


Tags : ASP.NET ASP.NET Core AJAX MVC C# Visual Studio