Return a Zip file in Node with a Netlify function

In a recent journey with Node and Neltify, I wanted to return a Zip file when a Netlify function URL was visited.

The use-case I had was to build several different favicon files from an SVG and return the archive for the user.

This blog assumes knowledge in Node and with Netlify functions

Setup

Create a new netlify function in your functions directory (netlify/functions by default) - for the sake of this blog (and in my actual code) I created mine at zip/zip.js.

If you install and run the Netlify CLI you can run netlify dev to test your functions locally

To build and serve the Zip file, I used jszip - a library for creating and saving (or returning) .zip files.

To use this, it needs to be installed and saved in the package.json file, so in the root folder to your project, run

npm i --save jszip

We can then set up the skeleton of our Netlify function, by including the library and returning a JSON object to ensure our file is working correctly

const JSZip = require("jszip");

exports.handler = async (event, context) => {
	return {
		body: JSON.stringify({message: "Zip file"}),
		statusCode: 200,
	}
};

By visiting your base URL (e.g. http://localhost:8888) followed by .netlify/functions/zip - you should get some JSON returned with message: "Zip file"

Building the Zip

To build the contents of the Zip file, the JSZip package includes a file() function - this allows you to specify the filename and contents.

For example, if you were to specify the following:

const zip = new JSZip();
zip.file('new.txt', 'hello everyone!');

Your Zip file would contain one file called new.txt with the contents of that above.

This works with images too - in my code, I used the sharp pacakge to resize and image and add it to the Zip file.

You can also create folders with zip.folder() if desired. More examples and documention can be found on the JSZip website.

Return the Zip file

This was the thing I struggled with the most - returning the Zip file. Most examples showed saving a Zip file to the file system - however Netlify doesn't allow that so we need to return it to the user straight away.

Once all the files are added to the Zip, we need to generate a base64 version of the Zip file, and wait for it, using the generateAsync() function

let zipFile = await zip.generateAsync({type: 'base64'});

We can then return the Zip file, informing Netlify that it is base64 encoded, so it serves it correctly (not the filename of the Zip downloaded is specified in the Content-disposition header)

return {
	headers: {
		'Content-Type': 'application/zip, application/octet-stream',
		'Content-disposition': `attachment; filename=${`zipfile_${new Date().toJSON()}.zip`}`
	},
	body: zipFile,
	statusCode: 200,
	isBase64Encoded: true,
}

Full code

So with everything in place, the following code generates a Zip file with a text and SVG file and returns the archive for the user to download. At no point is anything saved on the filesystem.

For a more complete example, one can be found in the Favicon Generator Github repository

const JSZip = require("jszip");

exports.handler = async (event, context) => {
	// Create archive
	const zip = new JSZip();

	// Add the files
	zip.file('new.txt', 'The other file is an SVG');
	zip.file('test.svg', '<svg width="300" height="200" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="red"/><circle cx="150" cy="100" r="80" fill="green"/><text x="150" y="125" font-size="60" text-anchor="middle" fill="#fff">SVG</text></svg>');

	// Build the Zip file
	let zipFile = await zip.generateAsync({type: 'base64'});

	// Return the Zip file with `zipfile_[DATE]` as the filename
	return {
		headers: {
			'Content-Type': 'application/zip, application/octet-stream',
			'Content-disposition': `attachment; filename=${`zipfile_${new Date().toJSON()}.zip`}`
		},
		body: zipFile,
		statusCode: 200,
		isBase64Encoded: true,
	}
};

Let me know how you get on and if you are using this code.

View this post on Github

You might also enjoy…

Mike Street

Written by Mike Street

Mike is a CTO and Lead Developer from Brighton, UK. He spends his time writing, cycling and coding. You can find Mike on Mastodon.