Aajx File Upload with Drag and Drop

Jason Ge
5 min readSep 30, 2021

In this article, we will discuss how to do ajax file upload with drag and drop box.

File input

We can use HTML input tag with type=“file” and attribute “multiple” to enable multiple file selection.

<input type=”file” id=”files” multiple style=”display: none;” />

It display as a button like following:

Click this button will open the “File Open Dialog”. However, there are no easy way to customize the look and feel of this button. In most of cases, we hide this button and trigger the “File Open Dialog” using other element. We will show you shortly.

Drop Zone

Drop zone in the web page can be just a div element. Normally we would decorate this element to visually indicate this is a drop zone. Browsers default drop behavior is to open the file. We need to disable this default behavior and provide our own logic to handle the files drop. In order for the drop zone to receive the files drop, it would need to capture following events:

  1. drop event: This event fires when an item is dropped on a valid drop target. The files are in property of event.dataTransfer.files . This property contains an array of File object.
  2. dragover event: This event fires when an item is being dragged over a valid drop target. If the item is not released, this event will keep firing every few hundred milliseconds. In this event handler, we need to call preventDefault() to disable browser default behavior (open the file). We can also do some styling changes to visually indicate the files are over the drop zone and ready to drop, such as change the background color and border width.
  3. dragenter event: This event fires only once when a dragged item enters a valid drop target. In this event handler, we need to call preventDefault() to disable browser default behavior.
  4. dragleave event: This event fires only once when a dragged item leaves a valid drop target. We can remove the drop zone style changes we did in the dragover event handler.
  5. click event: This event fires when user click in any area inside the drop zone. In this event handler, we can trigger the “File Open Dialog” programmatically so user can select files without drag and drop.

Handle Drop Event

As previously mentioned, the drop event has files inside event.dataTransfer.files property. This property contains an array of File object. Each file object has following properties:

  1. name: The name of the file
  2. size: The size of the file in bytes
  3. type: The MIME type of the file

Inside the drop event, we need to do following things:

  1. Restore the dropzone style to normal.
  2. Call pereventDefault() function to prevent browser default behavior for the file drop (opening the file).
  3. Go through the event.dataTransfer.files collection to make sure the file type is what you expected, the file size does not exceed your maximum allowed size, etc.
  4. Once the files validation passed, display all the file details and Upload button for user to confirm and upload.

Prevent Drop Outside DropZone

When user drag the files, we do not want user to drop the files outside the dropzone we defined. The browser default behavior is to open these files. We should add logic if user drop the file outside the drop zone, nothing would happen. In order to achieve this, we need to capture the all the events we mentioned above in Window level.

window.addEventListener(“dragenter”,function(e){
if (e.target.id != “dropZone” && e.target.id != “dropZoneText”)
{
e = e || event;
e.dataTransfer.dropEffect = “none”;
e.preventDefault();
}
},false);
window.addEventListener(“dragover”,function(e){
if (e.target.id != “dropZone” && e.target.id != “dropZoneText”)
{
e = e || event;
e.dataTransfer.dropEffect = “none”;
e.preventDefault();
}
},false);
window.addEventListener(“drop”,function(e){
if (e.target.id != “dropZone” && e.target.id != “dropZoneText”)
{
e = e || event;
e.preventDefault();
}
},false);

Inside the dragover and dragenter event handlers, we check if the element is not inside the drop zone, we set the e.dataTransfer.dropEffect = "none" which will change the mouse cursor to “not allowed”. We also call the e.preventDefault() to prevent default browser behavior.

File Upload

The ajax file upload uses FormData interface to upload file. FormData interface has append() method:

formData.append(name, value, filename);

We can add the file to FormData object as following:

const formData = new FormData();
formData.append(“file”, file.data, file.data.name);

The formData object can then be provided to JQuery ajax call as data property.

$.ajax({
type: "POST",
url: 'https://localhost:44384/file/upload',
xhr: function () {
var xhr = $.ajaxSettings.xhr();
if (xhr.upload) {
xhr.upload.addEventListener('progress', self.progressHandling(file.fileIndex), true);
}
return xhr;
},
success: function(result) {
// Add success logic here
},
error: function(error) {
console.log(error);
// Add error handling here
},
async: true,
data: formData,
cache: false,
contentType: false,
processData: false,
timeout: 60000
});

Please note you have to set contentType to false and processData to false in order for it to work:

  • Setting the contentType to false tells jQuery to not set any content type header
  • Setting processData to false will prevent jQuery from transforming the data into a string. See the jQuery docs for more info.

Tracking Upload Progress

For large file upload, we may want to show the upload progress to user. In order to do that, you need to capture the jQuery xhr callback function. Inside the callback function, add the event listener for the progress event. The progress event has only one parameter of type ProgressEvent, which as two properties: loadedand total. Base on these two properties, we can easily calculate the uploading percentage.

progressHandling = function (index) {
const self = this;
return function (event) {
var percent = 0;
var position = event.loaded || event.position;
var total = event.total;
if (event.lengthComputable) {
percent = Math.ceil(position / total * 100);
}
console.log(self.files[index].data.name, “upload percentage: “, percent);
$("#fileList tr[id='fileNum-" + index + "'] .progress-bar").css("width", +percent + "%");
$("#fileList tr[id='fileNum-" + index + "'] .progress-bar").text(percent + "%");
}
}

In case of multiple file uploads, we need to know which file we are working one, so we need extra parameter: file index. However, the progress event accept only one parameter of type ProgressEvent. We need to use JavaScript closure technique to achieve our goal. The progressHandlingfunction accept file index as parameter and return a function to handle the progress event. The function that handles the progress event will be able to access the file index parameter in the outer function.

You can see how the page looks like in JSFiddler: https://jsfiddle.net/wnms2000/aqL7pfcm/3. Of course, the Upload button would not work since there is no backend API to handle the upload request.

Web API

On the web api side, we can define a method that accept IFormFile parameter. The parameter name should be the same as the name parameter we used in formData.append() method (in our case, it is called “file”). Following is the code snippet to handle the ajax file upload in asp.net core 3.1 Web API :

You can download the code (front end and back end) from Github: https://github.com/jason-ge/AjaxFileUpload. For testing, open the solution with Visual Studio 2019 and run the Web API project. Open the ajax_fileupload.html file in browser. Drag and drop multiple files into the drop zone and click the “Upload” button to see how it works.

Happy coding!

--

--

Jason Ge

Software developer with over 20 years experience. Recently focus on Vue/Angular and asp.net core.