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 with 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 what 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

Now, if you click the submit button, the form is just posted to itself as the action attribute is not defined. We want to ensure that when the form is submitted, the selected file is uploaded to the server asynchronously (without refreshing the page) using the 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')

Next, 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 to 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, extract the files if available, and make them available under the 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 uploads 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 uploads as well as multiple files uploaded 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 an error if they select the 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.

Read 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.