Send a File With Axios in Node.js

maximization

Maxim Orlov

Posted on May 31, 2021

Send a File With Axios in Node.js

This article was originally published a day earlier at https://maximorlov.com/send-a-file-with-axios-in-nodejs/

Programatically sending requests in Node.js can be a frustrating experience. First, you have to choose one out of many request libraries in the ecosystem. Second, they each have a slightly different API which is confusing when you're switching.

You also have to make sure the request is formatted in a specific way for the 3rd party on the receiving end to accept it.

Just as you're starting to get the hand of axios, you soon find out there are (subtle) usage differences depending on whether you're in the browser or Node.js.

What a pain.

Only if sending files with axios in Node.js would be as easy as taking a walk in the park.

Well, it can be.

In this article, you'll learn how to send files and associated data by constructing a form. We'll cover the two file types ā€” Buffers and Streams, and how to work with them.

Construct a form with form-data library

Before sending a file with axios, you first need to create a form and append the file to it. Axios can be used both in the frontend as backend and the library doesn't differentiate between the two. Therefore, sending a file with axios in Node.js is similar to sending a file with axios in the browser.

Because we don't have access to the FormData interface in Node.js as we do in the browser, we use the form-data library to construct a form. This is similar to a <form> element with encoding type set to "multipart/form-data" in the browser.

To construct a form, create a new instance and use the append(name, value) method to add a file and additional fields.

// `form-data` library gives us a similar API in Node.js to the `FormData` interface in the browser
const FormData = require('form-data');

// Create a new form instance
const form = new FormData();

// Append text fields to the form
form.append('productName', 'Node.js Stickers');
form.append('productDescription', 'Cool collection of Node.js stickers for your laptop.');

// `file` can either be a Buffer or a Stream
// āš ļø don't forget the 3rd argument, the file name, when appending a file
form.append('productImage', file, 'stickers.jpg');
Enter fullscreen mode Exit fullscreen mode

Notice that when adding a file to the form, the append function takes three arguments instead of two. The third argument is the file name and if it's missing, the file won't send properly so make sure to pass it along.

The second argument is the file itself, which can either be a Buffer or a Stream. Let's look at a few real examples of how and why you would use either two.

File as a Buffer

A file buffer (or blob) is what you'll encounter most often when dealing with files. It's essentially the entire file stored in binary format in the application's memory.

If you're familiar with multer, it uses MemoryStorage by default which is essentially storing the files in memory as a Buffer. Reading a file from disk with fs.readFile() also gives you the file stored as a Buffer.

In both cases, you can append the file buffer to the form:

const form = new FormData();

// File parsed by multer from incoming request
const file = req.file;
form.append('file', file.buffer, file.originalname);

// or read from disk
const file = await fs.readFile('./my-image.jpg');
form.append('file', file, 'my-image.jpg');
Enter fullscreen mode Exit fullscreen mode

File as a Stream

You can also append a file as a stream to the form. This is useful when, for example, the file is fetched from an external resource. You can then proxy the file directly to another API without storing it locally.

// Fetch external image as a stream
const response = await axios.get('https://i.imgur.com/8uJcFxW.jpg', { responseType: 'stream' });

const form = new FormData();
// Pass image stream from response directly to form
form.append('image', response.data, 'kitten.jpg');
Enter fullscreen mode Exit fullscreen mode

Another good example is when you're dealing with large files. Using streams instead of buffer could prevent your application from consuming too much memory and eventually crashing.

// Open file as a readable stream
const fileStream = fs.createReadStream('./large-file.zip');

const form = new FormData();
// Pass file stream directly to form
form.append('largeFile', fileStream, 'large-file.zip');
Enter fullscreen mode Exit fullscreen mode

To send multiple files, you simply append them one by one to the form.

Send the form with axios

Now let's send the form with axios. The axios API for sending a POST request is:

axios.post(url[, data[, config]]), where:

  • url - server URL that will be used for the request
  • data (optional) - the data to be sent as the request body
  • config (optional) - configuration object where you can set the request headers, amongst others

While the second and third arguments are optional, their order is important. Axios will always assume the second argument is the data you want to send with the request. It's a common mistake to pass the config object as the second argument instead of the third argument.

To send a form with axios in Node.js, you have to grab the form boundary and add it to the request.

The getHeaders() method on the form returns an object with Content-Type header set to multipart/form-data plus a unique boundary:

// form.getHeaders() gives you the Content-Type header with a unique boundary
console.log(form.getHeaders());
// {
//   'content-type': 'multipart/form-data; boundary=--------------------------339261229255605205279691'
// }
Enter fullscreen mode Exit fullscreen mode

When you create a form instance, internally it generates and uses a unique boundary. If you set the Content-Type header with a random boundary you created, it won't match the form's boundary and the request won't parse correctly. Therefore, make sure to always use the form's boundary.

Use the destructuring assignment to set the Content-Type header in the config parameter (3rd argument). This allows you to add additional headers if you wish to do so.

Here's how to send a form with axios:

// When using axios in Node.js, you need to set the Content-Type header with the form boundary
// by using the form's `getHeaders()` method
const response = await axios.post(url, form, {
  headers: {
    ...form.getHeaders(),
    Authorization: 'Bearer ...', // optional
  },
});
Enter fullscreen mode Exit fullscreen mode

Manually setting the Content-Type header is only needed in the backend. In the frontend, the browser does this automatically for you so you shouldn't set Content-Type yourself otherwise the file won't send properly.

Everything put together

To summarise, sending a file with axios in Node.js requires you to do two important things:

  1. Create a form with the form-data library
  2. Grab the Content-Type header with the form's boundary with form.getHeaders() and assign it to the axios request

We've looked at different ways to append a file to a form, either as a Buffer or a Stream. Below is a complete example that reads a file from disk into a Buffer and sends it to an external API with axios.

const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs/promises');

// Read image from disk as a Buffer
const image = await fs.readFile('./stickers.jpg');

// Create a form and append image with additional fields
const form = new FormData();
form.append('productName', 'Node.js Stickers');
form.append('productDescription', 'Cool collection of Node.js stickers for your laptop.');
form.append('productImage', image, 'stickers.jpg');

// Send form data with axios
const response = await axios.post('https://example.com', form, {
  headers: {
    ...form.getHeaders(),
    Authentication: 'Bearer ...',
  },
});
Enter fullscreen mode Exit fullscreen mode

How do you upload a file in Node.js?

Use the FREE request parsing guide and implement seamless working Node.js APIs that follow the latest best practices.

šŸ‘‰šŸ¼ Grab your copy of the FREE Request Parsing in Node.js Guide

šŸ’– šŸ’Ŗ šŸ™… šŸš©
maximization
Maxim Orlov

Posted on May 31, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related