File upload is the most common functionality in modern web applications. Many applications allow users to upload an avatar or an attachment to perform some backend tasks. Writing the code to upload a file asynchronously to a server looks like a challenging task.

In this article, I'll explain how to build an HTML form, send the selected files to the server with JavaScript, and process the upload file in Node.js.

Building an HTML Form

Let us start building a simple HTML form that has two elements: a <input> tag to allow the user to select a file from the local computer and a <button> tag to submit the form. Here is how it looks like:

<form method="POST" enctype="multipart/form-data">
    <input type="file" name="file">
    <button type="submit" role="button">Upload File</button>
</form>

Sending Form Data with JavaScript

Right now, if you click on the submit button, the form is just posted to itself as the action attribute is not defined. We want to make sure that when the form is submitted, the selected file is uploaded to the server asynchronously (without refreshing the page) using JavaScript XHR object.

Let us create a new file called upload.js and add a reference to it in your HTML file:

<script src="upload.js"></script>

Next, define two variables. The URL where your want to post the form data, and the DOM element for the form:

// define URL and for element
const url = "http://localhost:3000/upload-avatar";
const form = document.querySelector('form');

Now add an event listener to the form to capture the form submission event. Also, make sure that the default action is prevented from firing:

// add event listener
form.addEventListener('submit', e => {

    // disable default action
    e.preventDefault();

    // ....
});

Next, create a new instance of FormData and add the selected file into it:

// collect files
const files = document.querySelector('[name=file]').files;
const formData = new FormData();
formData.append('avatar', files[0]);

// ....

Finally, use the built-in XHR object to POST the data to the URL we defined above, and print the response on the console:

// post form data
const xhr = new XMLHttpRequest();
xhr.responseType = 'json';

// log response
xhr.onload = () => {
    console.log(xhr.response);
};

// create and send the reqeust
xhr.open('POST', url);
xhr.send(formData);

Here is the complete upload.js file:

// define URL and for element
const url = "/upload-avatar";
const form = document.querySelector('form');

// add event listener
form.addEventListener('submit', e => {

    // disable default action
    e.preventDefault();

    // collect files
    const files = document.querySelector('[name=file]').files;
    const formData = new FormData();
    formData.append('avatar', files[0]);

    // post form data
    const xhr = new XMLHttpRequest();

    // log response
    xhr.onload = () => {
        console.log(xhr.responseText);
    };

    // create and send the reqeust
    xhr.open('POST', url);
    xhr.send(formData);
});

The response returned by the Node.js API is a JSON object. Right now, we are only printing the response to the console. Take a look at this guide to learn about several ways to handle the JSON response in XHR.

Processing Form Data with Node.js

For handling the file upload on the server-side through Node.js and Express, I've already written a detailed article. I won't go into detail on how to set up the Node.js application and install all required packages. Please check the tutorial to learn all these things.

The tutorial uses the express-fileupload middleware to handle multipart/form-data requests, extracts the files if available, and make them available under req.files property.

You can install express-fileupload in your project by typing the following command:

$ npm install express-fileupload --save

Next, add the following Express route to process and save the file submitted by the above JavaScript code:

app.post('/upload-avatar', async (req, res) => {
    try {
        if(!req.files) {
            res.send({
                status: false,
                message: 'No file uploaded'
            });
        } else {
            // use the name of the input field (i.e. "avatar") 
            // to retrieve the uploaded file
            let avatar = req.files.avatar;
            
            // use the mv() method to place the file in 
            // upload directory (i.e. "uploads")
            avatar.mv('./uploads/' + avatar.name);

            //send response
            res.send({
                status: true,
                message: 'File is uploaded'
            });
        }
    } catch (err) {
        res.status(500).send(err);
    }
});

The above is the minimum code required to handle files in a Node.js application.

Uploading Multiple Files

The above example explains how to upload a single file in JavaScript. What if you want to upload multiple files at once? No worries. With a few changes, we can adjust the above code to support multiple files upload.

First of all, update the <input> tag to allow the user to select multiple files:

<input type="file" name="file" multiple>

Change the form submission URL to the one which handles multiple files upload:

const url = "http://localhost:3000/upload-photos";

Next, update the FormData part to send all selected files instead of just one:

Array.from(files).forEach(file => {
    formData.append("photos", file);
});

Finally, create a new Express route that accepts multiple files and upload them to the server:

app.post('/upload-photos', async (req, res) => {
    try {
        if (!req.files) {
            res.send({
                status: false,
                message: 'No file uploaded'
            });
        } else {
            let data = [];

            //loop all files
            _.forEach(_.keysIn(req.files.photos), (key) => {
                let photo = req.files.photos[key];

                //move photo to uploads directory
                photo.mv('./uploads/' + photo.name);
            });

            //return response
            res.send({
                status: true,
                message: 'Files are uploaded'
            });
        }
    } catch (err) {
        res.status(500).send(err);
    }
});

Congratulations! You can now upload any number of files at once.

Conclusion

That's all folks. In this tutorial, you have learned how to upload a file using JavaScript built-in XMLHttpRequest object and Node.js on the server-side. We looked at both single file upload as well as multiple files upload at once.

The aim of this article was to explain the basic steps required to successfully upload a file in JavaScript and Node.js. For a real-world application running on the production server, there should be some validation steps. The user should be shown in an error if they select a wrong type of file or the file size exceeds the allowed limit.

XMLHttpRequest also provides events to track file download and upload progress. Check out this guide to learn more about it.

Take a look at this guide to learn how to handle files upload through Fetch API, a modern alternative to XHR.

✌️ Like this article? Follow me on Twitter and LinkedIn. You can also Subscribe to RSS Feed.

Last Updated: