Load remote images and put them into a zip then the user can get the archive

Posted on August 30, 2021

I've spend a reasonable hour to crack this, apparently, simple task.

I wanted to download a bunch of images from the network using axios in Node.js in an Express.js app, to put those images into a zip file in order to let the user download it right away.

Sounds simple right? Well, after a good number of attempts, here mysolution:

The journey

Let's load a bunch of images using axios

const axios = require('axios')
const proms = []

for (const i = 0; i < urls.length; i++) {
  proms.push(axios(urls[i], { responseType: 'arraybuffer' }))
}

const images = await Promise.all(proms)

Here we have a key part to pay attention: arraybuffer and not blob since we are in Node.js.

Then, let's prepare the zip file using jszip:

const JSZip = require('jszip')
const zip = new JSZip()
const fold = zip.folder('images')

images.forEach((d, i) => {
    fold.file(`img${i}.jpg`, d.data, { binary: true })
})

Here another important part. We can use the Blob data (that is an ArrayBuffer) directly by setting the option binary:true.

Finally, let's close the zip file:

const fzip = await zip.generateAsync({ type: 'nodebuffer' })

Since we are in Node.js, JSZip has a specific encoding, nodebuffer.

Last bit, let's send the file buffer to the user:

res.setHeader('Content-Disposition', `attachment; filename="archive.zip"`)
res.setHeader('Content-Type', 'application/zip')
res.send(fzip)

Since we are in Node.js, this example uses Express.js.

Kudos to this article that allowed me to understand the issues I was facing because:

There is natively no Blob object in node.js, so it makes sense axios didn't monkey-patch it just so they can return a response no-one else would be able to consume anyway.

From a browser, you'd have exactly the same responses: