Blog Post

How to download multiple files with JavaScript

Illustration: How to download multiple files with JavaScript

While working with Nutrient Web SDK, you may find yourself needing to download multiple files from a server — perhaps to merge documents in a document operation, or to create multiple image annotations. Since the corresponding API methods require that you pass the data of these files — passing their URLs isn’t enough — you normally need to perform multiple requests to retrieve those files.

However, it’s also possible to gather all those files with a single request, provided you have control over the server. In this post, we’ll see how it’s done on the server side and the client side.

Overview

Uploading multiple files to a server using JavaScript is done by appending the files to a FormData object:

const formData = new FormData();
formData.append('file1', blob1, 'file1.pdf'); // blob1 is a `Blob` object.
formData.append('file2', blob2, 'file2.pdf'); // blob2 is another `Blob` object.
fetch('https://example.com/upload/', {
	method: 'post',
	body: formData,
});

But how can we get multiple files from a server with a single request? Well, for starters, we need a server capable of responding with a multipart/form-data response. Spoiler: They usually aren’t.

You read that right. While sending multipart form data requests to the server is a common task performed everywhere, sending responses from the server with that same encoding is much less common. That said, let’s see how we can do it.

Server side

There are different libraries and APIs available for each programming language, and they enable you to work with HTTP headers and responses. In this article, we’ll use JavaScript for both the browser-side and server-side code examples so that the similarities with and differences to the client code become more evident.

What follows is an example of how a Node.js server could send a multipart form data response to a browser. The Node.js environment doesn’t include a built-in FormData class like the browser environment does, so we need another way to convert our file data to the multipart/form-data format.

Luckily, there’s an npm package — form-data — that will help us reshape our file data in the required way:

const fs = require('fs');
const http = require('http');
const FormData = require('form-data');

const app = http.createServer((req, res) => {
	console.log(`Request: ${req.url}`);
	const form = new FormData();

	// Append two files to the form.
	form.append('file1', fs.readFileSync('./path/to/file1.pdf'), {
		filename: 'file1.pdf',
		contentType: 'application/pdf',
		knownLength: fs.statSync('./path/to/file1.pdf').size,
	});
	form.append('file2', fs.readFileSync('./path/to/file2.pdf'), {
		filename: 'file2.pdf',
		contentType: 'application/pdf',
		knownLength: fs.statSync('./path/to/file2.pdf').size,
	});

	// Set headers for multipart response.
	res.writeHead(200, {
		'Content-Type': `multipart/form-data; boundary=${form.getBoundary()}`, // boundary must match the one generated by form-data
		'Content-Length': form.getLengthSync(),
		'Access-Control-Allow-Credentials': 'true',
		'Access-Control-Allow-Origin': '*', // Update based on your needs (for demo purposes, '*' is more flexible)
	});

	// Write the multipart form data response.
	res.write(form.getBuffer());
	res.end();
});

const port = 4000;
app.listen(port);
console.log(`Server listening on port ${port}`);

Just connecting to the server above will trigger a response that includes two files.

Client side

Now, dealing with a multipart form data response from the browser is something we already know how to do; we just need to combine two interfaces: Response and FormData (the latter being implemented by the former, at least in modern browsers).

Let’s apply the FormData interface to the server Response:

try {
	const res = await fetch('https://example.com/download-multiple', {
		mode: 'cors',
		headers: {
			Accept: 'multipart/form-data',
		},
	});
	const formData = await res.formData();
	console.log(Array.from(formData.values()));
} catch (error) {
	console.error(error.message);
}

The snippet above should log an array of objects with a key and a value (a file object) to the console.

Browser support

As mentioned above, all modern browsers support the FormData interface for the Response object, with some exceptions: Some old Safari versions may throw if the response’s formData() method is called, while IE11 doesn’t know anything about Response objects at all: The usual polyfills also aren’t prepared to handle binary data in the FormData response, so the only option in this case would be to parse the raw multipart body, which may or may not be worth the effort, depending on each scenario. In any case, it falls beyond the scope of this article.

Warning

Internet Explorer 11 is no longer supported in our Web SDK as of version 2022.5. Edge 18 is no longer supported in our Web SDK as of version 2023.2.

Conclusion

As we’ve seen, downloading multiple binary files by sending a single request to the server can be accomplished quickly and easily, and at Nutrient, we’ve been doing it in our JavaScript PDF library for some time now. If you’re interested in learning more about our product, we encourage you to check out our demo and give it a try.

FAQ

Here are a few frequently asked questions about downloading multiple files with JavaScript.

How can I download multiple files with a single request?

You can use a multipart/form-data response from the server to send multiple files in one request, provided the server supports it.

What should I do if my server doesn’t support multipart form data responses?

You’ll need to send separate requests for each file or consider bundling the files into a single compressed archive, such as a ZIP file.

How do I ensure CORS is properly configured for downloading files?

Make sure the server sends the correct Access-Control-Allow-Origin header and supports credentials if required for cross-origin requests.

Are there any browser limitations for handling formData() responses?

Yes, older browsers like IE11 do not support the formData() method on responses, so you may need to handle files differently or use polyfills.

Can I use this approach for large file downloads?

Yes, but for large files, ensure the server supports streaming, or consider downloading the files in smaller parts to avoid memory issues.

Author
Miguel Calderón Web Team Lead

As part of the Web Team, Miguel loves finding new challenges and trying to determine what the newest technologies can do to improve our product. Writing fiction, reading, and traveling are his passions the rest of the time.

Related products
Share post
Free trial Ready to get started?
Free trial