Javascript File Uploading with Fetch, Async & Await

javascript
Published on April 13, 2020

fetch() is a Promise-based API for making HTTP requests in browsers. Being Promise-based, we can use async / await to write "synchronous" styled functions for better code management.

Sample Code

<input type="file" id="file-to-upload" />
<button id="upload-button">Upload</button>
document.querySelector("#upload-button").addEventListener('click', async function() {
	let upload = await uploadFile();
	
	if(upload.error == 0)
		alert('File uploaded successful');
	else if(upload.error == 1)
		alert('File uploading failed - ' + upload.message);
});

// async function managing upload operation
async function uploadFile() {
	// function return value
	let return_data = { error: 0, message: '' };

	try {
		// no file selected
		if(document.querySelector("#file-to-upload").files.length == 0) {
			throw new Error('No file selected');
		}
		else {
			// formdata
			let data = new FormData();
			data.append('title', 'Sample Title');
			data.append('file', document.querySelector("#file-to-upload").files[0]);

			// send fetch along with cookies
			let response = await fetch('/upload.php', {
		        method: 'POST',
		        credentials: 'same-origin',
		        body: data
		    });

	    	// server responded with http response != 200
	    	if(response.status != 200)
	    		throw new Error('HTTP response code != 200');

	    	// read json response from server
	    	// success response example : {"error":0,"message":""}
	    	// error response example : {"error":1,"message":"File type not allowed"}
	    	let json_response = await response.json();
	        if(json_response.error == 1)
	           	throw new Error(json_response.message);	
		}
	}
	catch(e) {
		// catch rejected Promises and Error objects
    	return_data = { error: 1, message: e.message };
    }

	return return_data;
}
  • All async functions return a Promise which resolves to the value returned by the function, or rejected with an uncaught exception in the function.
    In this case the async function "uploadFile" returns a Promise that is always resolved with an object denoting uploading success or error (we are handling exceptions in the function, so the returned Promise is never rejected).
  • The Promise returned by fetch() is rejected only on network errors — DNS lookup error, user is offline etc. The Promise will be resolved even on error HTTP codes such as 404 and 500.
  • response.status returns the HTTP status code of the response.
  • For this current example, the server sends a JSON response of the type {"error":0,"message":""} / {"error":1,"message":"Error Message"} with HTTP status 200.
  • response.json() returns a Promise that resolves to the parsed JSON object.
  • await operator waits for the given Promise to be fulfilled. It needs to be placed inside an async function.
  • FormData object automatically sets the Content-Type of the upload request as "multipart/form-data".

Getting Upload Progress for Fetch Request

Currently fetch() does not support tracking upload progress. However XMLHttpRequest has an upload progress event.

In this Tutorial