How to create an upload progress bar with javascript

Updated: 30-Mar-2022 / Tags: PHP and AJAX / Views: 2238 - Author: George

Introduction

Hello everyone, ever wonder how we can track the upload process and progress of a file. What is the code behind a progress bar, how does it works.
In this full stack web development tutorial we are going to solve the mystery 😯 that lies behind the tracking of an upload progress bar.
Lets start.

The project's file structure

As you can see in the image below in our projects root we have an upolads/ folder, there we will send our uploaded files. We have an index file (which is our home page), a script.js file to write the AJAX request, a script.php file to catch the AJAX request and send the file to the uploads/ folder. And a stylesheet, which i will not write about, because there is nothing important in that file that we need for this tutorial.
If you download the files you will get this exact folder structure.

an image of a full stack project folder structure

The upload form

Let's take a look in the index.php file and the form that we have here.

<body>
	<form name="upload-form">
		<label>Select file to upload</label>
		<input name="file" type="file">
		<input type="submit" name="submit" value="Upload file">

		<progress value="0" max="100"></progress>

		<p class="error"></p>
		<p class="success"></p> <!-- success placeholder -->
	</form>
  • In line 2 we have the opening form tag, and as you see we have only the name attribute set. Usually we have something like this, action="" method="post" enctype="multipart/form-data", but we don't need those attributes this time, because we are sending the file to the server with an AJAX request
  • In line 4 we have the input choose file button, with which we choose files from our computer.
  • Next in line 5 we have the form's submit button.
  • In line 7 we have the progress element, this is a valid HTML element. The value attribute is set to zero, that means that the starting value is zero. And the max value is set to 100, that means that the progress will fill up the whole width of the element.
  • And in line 9-10 we have an error and a success placeholder, where we print an error if there is no file selected and a success message when the file is moved in our uploads folder.

The javascript code

We are now in the script.js file where we will catch the form's submission, and we will send the file to the server with an AJAX HTTP Request.

document.forms['upload-form'].onsubmit = function(e){
	e.preventDefault();

	let error = document.querySelector(".error");
	let success = document.querySelector(".success");
	let file = this.file.files[0];
	error.innerHTML = "";

	if(!file){
		error.innerHTML = "Please select a file";
		return false;
	}

	let formdata = new FormData(); /* creating a new object */
	formdata.append("file", file);

	let http = new XMLHttpRequest();
	http.upload.addEventListener("progress", function(event){
		let percent = (event.loaded / event.total) * 100;
		document.querySelector("progress").value = Math.round(percent);
	});

	http.addEventListener("load", function(){
		if(this.readyState == 4 && this.status == 200){
			success.innerHTML = `File ${this.responseText} uploaded successfully`;
		}
	});

	http.open("post", "script.php");
	http.send(formdata);
}

Code breakdown

Let's analyze the javascript code line by line.

document.forms['upload-form'].onsubmit = function(e){
	e.preventDefault();
  • We are starting our code by assigning an onsubmit event listener to our upload form in line 1. We are targeting the form using the form's name attribute.

    In the function we pass-in as an argument the event object (e), because we want to stop the form submitting to the server in line 2, with the preventDefault() method.

Next we will target the error, and success placeholder elements, and the selected file.

	let error = document.querySelector(".error");
	let success = document.querySelector(".success");
	let file = this.file.files[0];
	error.innerHTML = "";
  • In line 4 i am targeting the error element and store it in the let error variable.
  • In line 5 i am targeting the success element and store it in the let success variable.
  • In line 6 we are targeting the selected file. The this keyword gives me access to the form.
    With this.file, we access the choose file input element, by targeting the input's name="file" attribute.
    Now when we select a file in the browser, the file object stores that file in an array called files[]. Since we can select only one file, the file exists in index zero [0] in the files[] array.
    So the let file variable is holding the selected file.
  • In line 7 we set the error element to an empty string. Every time the function runs the error element will be empty. That will clear any previous errors that were displayed.

Next we use the file variable to check if there is a file selected. If not we are targeting the error element to displaying an error, and stopping the function from executing with the return false statement.

	if(!file){
		error.innerHTML = "Please select a file";
		return false;
	}

Now to send a file to the server via Ajax we need to send it as an entry of a formdata object.

   let formdata = new FormData(); /* creating a new object */
	formdata.append("file", file);
  • In line 14 i will create a new FormData() object and i will store it in the formdata variable.
  • In line 15 i will use the .append() method, and i will add the selected file in the FormData object.
  • The FormData object will structure the data that contains, in the exact same way as an HTML form would. Therefore we can send the file with an AJAX request as formData, and catch it in the server with the php file.

Now that we have added the file in the formdata object, we are ready to write an XMLHttpRequest, and send the file to the back-end, and the php file.

	let http = new XMLHttpRequest();
  • In line 17 i create a new XMLHttpRequest() object and store it in the http variable.
    Now the http variable is holding all the properties and methods of the XMLHttpRequest object.

The following code block will do all the work that we need to track the uploaded file, and display the progress in the progress bar element.

	http.upload.addEventListener("progress", function(event){
		let percent = (event.loaded / event.total) * 100;
		document.querySelector("progress").value = Math.round(percent);
	});
  • Line 18. The XMLHttpRequest object has a property called upload. I will assign an onprogress event-listener to the upload property and run a function in which i will pass-in as an argument the event object.
  • Inside the function, in line 19, i will make use of the .loaded and .total properties of the event object.
    I will divide the loaded number, with the total number, and i will multiply the result times 100. That will give me the upload percentage at any given time until the file is 100% uploaded. Hope it makes sense.
  • And in line 20 i will round the percentage number with the Math.round() method and i will display it in the progress element.

In this code block we are catching the response from the server ( from the php file ).

   http.addEventListener("load", function(){
		if(this.readyState == 4 && this.status == 200){
			success.innerHTML = `File ${this.responseText} uploaded successfully`;
		}
	});
  • To catch the server's response we have to use the http.load event-listener like we do in line 23.
    The load event-listener is fired when the transaction with the server is completed.
  • Next in line 24, we are checking the this.readyState and the this.status properties, the first value must be 4, and the second value 200, to have a successful server response.

    If the keyword this is confusing you, you can write the code also this way http.readyState or http.status.

    Have in mind that the keyword "this" gives us access to the methods and properties of the object that it belongs to. Here the "this" keyword belongs to the XMLHttpRequest object, therefore we can use it in it's place.

Next and final task is to send the formdata to the server.

	http.open("post", "script.php");
	http.send(formdata);
}
  • In line 22 i prepare the http request with the .open() method.

    In the first argument we have the http method set to "post", and in the second argument we have the php file's name, "script.php".

  • Next in line 23 i use the .send() method to execute the http request. As an argument we pass in the formdata variable which is holding the formData object and our selected file.
  • Next we have to go to the php file and catch that http request.

PHP file

In the script.php file we are going to catch the request. We are going to move the uploaded file to the uploads/ folder, and we are going to send to the javascript file the name of the uploaded file as a server response.

<?php
	if(isset($_FILES)){
		$temp_file = $_FILES['file']['tmp_name'];
		$uploads_folder = "uploads/{$_FILES['file']['name']}";
		$upload = move_uploaded_file($temp_file, $uploads_folder);
		if($upload == true){
			echo $_FILES['file']['name'];
		}
	}
  • First of all we have to check if there is an incoming file. We check the $_FILES super global variable in line 2.
  • In line 3, if the file exists, we access the files temporary location and store it in the $temp_file variable.
  • Next in line 4 we are setting in a variable our uploads/ folder.
  • In line 5 we move the file from the temporary location to the uploads/ folder, and we assign the returned value in the $upload variable.

    The move_uploaded_file() function takes as the first argument the temporary location of the file, and as second argument the destination folder. If the process is successful the function returns true.

  • In line 6-8 we check if the returned value is true, and if so, we echo out the file's name. The echo statement is the server's response, this is what we catch in the javascript file.

Summary

We saw how to send a file to the server with an XMLHttp Reqest and in adition calculate the upload progress and display it in a progress bar.

Last Words

Thanks for reading, i hope you find the article helpful.
Please leave a comment if you find any error's, so i can update the page with the correct code.

Source code

You can download the source code and use it in any way you like.

Times downloaded: 450

Buy me a coffee

If you like to say thanks, you can buy me a coffee.

Buy me a coffee with paypal

Comment section

You can leave a comment, it will help me a lot.

Or you can just say hi. 😉