AJAX File Download with Progress Bar in Pure Javascript

javascript
Published on February 6, 2018

Currently most of the web applications show a normal link <a href="http://download-link"> for the users to download a file. When the link is clicked, the browser detects that it is a file, and allows the user to download it. This is the most easiest way of allowing users to download a file.

But in some cases the web application may want to handle the download part itself, rather than leaving it to the browser. It may have its own reasons like showing the download progress of the file in the applicaton's UI itself. Another reason may be monetization - the application can show an advertisement to the user while the file is being downloaded.

This tutorial shows how to make an AJAX request to download a file, and showing the download percentage completed.

Demo

Starting Download..
Save File

Download Sample Codes

Download

Make sure files are run from a server.

Sending an AJAX Request

A XMLHttpRequest object is used to make a normal AJAX request. However when downloading binary files, the responseType property of the request object is set to blob.

var request = new XMLHttpRequest();
request.responseType = 'blob';

Possible values of responseType property are empty string (default), arraybuffer, blob, document, json, and text.

For simply downloading binary files use blob as the response type.

For complex cases where you need to download a file and then manipulate the file's bytes - for example if you are building a Javascript "PPT Viewer", set arraybuffer as the response type.

Showing the Download Progress

You can set progress event on the XMLHttpRequest object. Through this you can get the percentage of the file downloaded.

var request = new XMLHttpRequest();

request.addEventListener('progress', function(e) {
	var percent_complete = (e.loaded / e.total)*100;
	console.log(percent_complete);
});

Getting Status of the AJAX Request

You can set readystatechange event on the XMLHttpRequest object. Through this you can find whether download is being started, happenning or has been completed.

var request = new XMLHttpRequest();

request.addEventListener('readystatechange', function(e) {
	if(request.readyState == 2 && request.status == 200) {
		// Download is being started
	}
	else if(request.readyState == 3) {
		// Download is under progress
	}
	else if(request.readyState == 4) {
		// Downloaing has finished

		// request.response holds the file data
	}
});

Complete Codes

<button id="download-button">Download</button>

<!-- This will hold the local download file -->
<a id="save-file">Save File</a>
// This will hold the the file as a local object URL
var _OBJECT_URL;

// Call an AJAX
document.querySelector('#download-button').addEventListener('click', function() {
	var request = new XMLHttpRequest();
    
    request.addEventListener('readystatechange', function(e) {
    	if(request.readyState == 2 && request.status == 200) {
    		// Download is being started
    	}
    	else if(request.readyState == 3) {
    		// Download is under progress
    	}
    	else if(request.readyState == 4) {
    		// Downloaing has finished

    		_OBJECT_URL = URL.createObjectURL(request.response);

    		// Set href as a local object URL
    		document.querySelector('#save-file').setAttribute('href', _OBJECT_URL);
    		
    		// Set name of download
    		document.querySelector('#save-file').setAttribute('download', 'img.jpeg');
    		
    		// Recommended : Revoke the object URL after some time to free up resources
    		// There is no way to find out whether user finished downloading
    		setTimeout(function() {
    			window.URL.revokeObjectURL(_OBJECT_URL);
    		}, 60*1000);
    	}
    });
    
    request.addEventListener('progress', function(e) {
    	var percent_complete = (e.loaded / e.total)*100;
    	console.log(percent_complete);
    });
    
    request.responseType = 'blob';
    
    // Downloading a JPEG file
    request.open('get', 'img.jpeg'); 
    
    request.send(); 
});

The above codes downloads the file and sets the object URL - basically the user has to click twice to download a file. You can also create the link dynamically, and invoke a click on it so that the user has to click once to download the file.

IE 10 & 11

The download attribute of the <a> element won't work in IE 10 & 11. See Saving files locally using Blob and msSaveBlob and msToBlob method for alternatives.

Useful Links

Sending and Receiving Binary Data
In this Tutorial