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 !!