Blog Post

How to Download Multiple Files with JavaScript

Illustration: How to Download Multiple Files with JavaScript

While working with PSPDFKit for Web, 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();
	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,
	});
	res.writeHead(200, {
		'Content-Type': `multipart/form-data; boundary=${form.getBoundary()}`,
		'Content-Length': form.getLengthSync(),
		'Access-Control-Allow-Credentials': 'true',
		'Access-Control-Allow-Origin': 'https://example.com',
	});
	res.write(form.getBuffer());
	res.end();
});

const port = 4000;
app.listen(port);
console.log(`Server listening in 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 PSPDFKit, 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.

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 60-Day Trial Try PSPDFKit in your app today.
Free Trial

Related Articles

Explore more
PRODUCTS  |  Web

Introducing AI Document Assistant: Enhancing the Document Experience with Cutting-Edge AI Features

DEVELOPMENT  |  WebAssembly • JavaScript • Web • Document Processing

Leveraging WebAssembly in JavaScript for High-Performance Document Processing

DESIGN  |  Baseline UI • Web

Optimizing Icon Design: Our Journey to the Baseline UI Icon Set