Javascript AJAX File Upload With Progress Bar

javascript
Published on February 4, 2018

AJAX file uploading in Javascript can be achieved using XMLHttpRequest & FormData objects.

Quick Sample Code

<input type="file" id="file-input" />
<button id="upload-button">Upload File</button>

<script>
	document.querySelector('#upload-button').addEventListener('click', function() {
		if(document.querySelector('#file-input').files.length == 0) {
			alert('Error : No file selected');
			return;
		}

		let file = document.querySelector('#file-input').files[0];
		let allowed_mime_types = [ 'image/jpeg', 'image/png' ];
		let allowed_size_mb = 2;
	
		if(allowed_mime_types.indexOf(file.type) == -1) {
			alert('Error : Incorrect file type');
			return;
		}

		if(file.size > allowed_size_mb*1024*1024) {
			alert('Error : Exceeded size');
			return;
		}

		let data = new FormData();
		data.append('file', document.querySelector('#file-input').files[0]);

		let request = new XMLHttpRequest();
		request.open('POST', 'upload.php'); 
		request.addEventListener('load', function(e) {
			console.log(request.response);
		});
		request.send(data);
	});
</script>

How to handle the uploaded file in PHP ? See FAQs at the end of this page.

How is File Uploading Done ?

File uploading in Javascript can be achieved by :

  • Choosing a file from the system using a <input type="file" /> tag.
  • Validating chosen file for type and size.
  • Sending a POST request using the XMLHttpRequest object with the file attached.

Step 1 — Choose a Local File

<input type="file" id="file-input" />
<button id="upload-button">Upload File</button>
  • To allow multiple files to be chosen, set the multiple attribute.

    <input type="file" multiple />
    
  • Restrictions on file types can be set by specifying the required MIME type in the accept attribute (although it is possible for the user to override it). Each allowed MIME type needs to be separated by a comma.

    <!-- Allow only JPEG & PNG files to choose -->
    <input type="file" accept="image/jpeg, image/png" />
    

Step 2 — Validate Chosen File for Type and Size

document.querySelector('#upload-button').addEventListener('click', function() {
	// user has not chosen any file
	if(document.querySelector('#file-input').files.length == 0) {
		alert('Error : No file selected');
		return;
	}

	// first file that was chosen
	let file = document.querySelector('#file-input').files[0];

	// allowed types
	let allowed_mime_types = [ 'image/jpeg', 'image/png' ];
	
	// allowed max size in MB
	let allowed_size_mb = 2;
	
	// validate file type
	if(allowed_mime_types.indexOf(file.type) == -1) {
		alert('Error : Incorrect file type');
		return;
	}

	// validate file size
	if(file.size > allowed_size_mb*1024*1024) {
		alert('Error : Exceeded size');
		return;
	}

	// validation is successful
	alert('You have chosen the file ' + file.name);

	// upload file now
});
  • The files property of the file input DOM element is an array of file objects representing the files selected by the user.
  • type property of chosen file object gives its MIME type.
  • size property gives its size in bytes.
  • name property gives its name.

Step 3 — Send an AJAX POST Request with File Attachment

let data = new FormData();

// file selected by the user
// in case of multiple files append each of them
data.append('file', document.querySelector('#file-input').files[0]);

let request = new XMLHttpRequest();
request.open('POST', 'upload.php'); 

// upload progress event
request.upload.addEventListener('progress', function(e) {
	let percent_complete = (e.loaded / e.total)*100;
	
	// percentage of upload completed
	console.log(percent_complete);
});

// AJAX request finished event
request.addEventListener('load', function(e) {
	// HTTP status message
	console.log(request.status);

	// request.response will hold the response from the server
	console.log(request.response);
});

// send POST request to server side script
request.send(data);
  • File uploading requires multipart/form-data HTTP POST request to the server. This can be achieved by sending a FormData object.
  • The progress event of the XMLHttpRequest.upload object listens for upload progress. A upload progress bar can be created utilizing this.
  • The load event handles competition of the AJAX request.

Other FAQs

How to handle the uploaded file using PHP ?
<?php
$allowed_file_types = ['image/jpeg', 'image/png'];
$allowed_size_mb = 2; 

// validate upload error
switch($_FILES['file']['error']) {
	// no error
	case UPLOAD_ERR_OK:
		break;

	// no file
	case UPLOAD_ERR_NO_FILE:
		exit('Error : No file send as attachment');

	// php.ini file size exceeded 
	case UPLOAD_ERR_INI_SIZE:
		exit('Error : File size exceeded as set in php.ini');

	// other upload error
	default:
        exit('Error : File upload failed');
}

// validate file type from file data
$finfo = finfo_open();
$file_type = finfo_buffer($finfo, file_get_contents($_FILES['file']['tmp_name']), FILEINFO_MIME_TYPE);
if(!in_array($file_type, $allowed_file_types))
	exit('Error : Incorrect file type');

// validate file size
$file_size = $_FILES['file']['size'];
if($file_size > $allowed_size_mb*1024*1024)
	exit('Error : Exceeded size');

// safe unique name from file data
$file_unique_name = sha1_file($_FILES['file']['tmp_name']);
$file_extension = strtolower(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION));
$file_name = $file_unique_name . '.' . $file_extension;

$destination = 'uploads/' . $file_name;

// save file to destination
if(move_uploaded_file($_FILES['file']['tmp_name'], $destination) === TRUE)
	echo 'File uploaded successfully';
else
	echo 'Error: Uploaded file failed to be saved';
?>
In this Tutorial