Creating a ZIP Archive and Downloading Multiple Files in Express.js

🚀 Introduction

In many web applications, users need to download multiple files at once. Instead of sending each file individually, it is more efficient to combine them into a single ZIP archive. Express.js, together with the archiver package, makes it easy to create ZIP files dynamically and stream them directly to the client without permanently storing the archive on the server.

Information

The archiver package creates ZIP archives on the fly, reducing storage usage and allowing users to download multiple files with a single request.

📋 Prerequisites

  • Node.js installed.
  • Express.js installed.
  • Basic Express.js server created.
  • Multiple files available in a server directory.

đŸ“Ļ Installing the Required Package

Install the archiver package to create ZIP archives programmatically.

Install archiver

npm install archiver

📁 Project Structure

express-app
downloads
index.js
package.json
report.pdf
invoice.xlsx
image.png

📝 Creating a ZIP Archive

Import the required modules, create an archive, and stream it directly to the client.

Create and Download ZIP Archive

const express = require('express');
const archiver = require('archiver');
const path = require('path');

const app = express();

app.get('/download-all', (req, res) => {
  res.attachment('documents.zip');

  const archive = archiver('zip', {
    zlib: { level: 9 }
  });

  archive.pipe(res);

  archive.file(
    path.join(__dirname, 'downloads', 'report.pdf'),
    { name: 'report.pdf' }
  );

  archive.file(
    path.join(__dirname, 'downloads', 'invoice.xlsx'),
    { name: 'invoice.xlsx' }
  );

  archive.file(
    path.join(__dirname, 'downloads', 'image.png'),
    { name: 'image.png' }
  );

  archive.finalize();
});

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

Success

Visiting http://localhost:3000/download-all downloads a ZIP archive containing all the selected files.

📌 Understanding the Code

StatementPurpose
res.attachment()Sets the response as a downloadable attachment.
archiver('zip')Creates a new ZIP archive.
archive.pipe(res)Streams the ZIP archive to the client.
archive.file()Adds an existing file to the archive.
archive.finalize()Completes the archive and begins streaming.

📂 Adding an Entire Directory

Instead of adding files individually, you can include an entire directory in the ZIP archive.

Archive a Folder

app.get('/download-folder', (req, res) => {
  res.attachment('downloads.zip');

  const archive = archiver('zip');

  archive.pipe(res);

  archive.directory(
    path.join(__dirname, 'downloads'),
    false
  );

  archive.finalize();
});

The second argument (false) places the folder contents directly in the root of the ZIP archive.

📄 Adding Files Dynamically

You can add files dynamically by iterating through a list of filenames.

Dynamic ZIP Creation

const files = [
  'report.pdf',
  'invoice.xlsx',
  'image.png'
];

files.forEach(file => {
  archive.file(
    path.join(__dirname, 'downloads', file),
    { name: file }
  );
});

archive.finalize();

âš ī¸ Handling Errors

Archive Error Handling

archive.on('error', (err) => {
  res.status(500).send(err.message);
});

Warning

Always handle archive errors to prevent incomplete downloads and unexpected server failures.

🔒 Validating Download Requests

When users choose which files to download, validate every filename before adding it to the archive.

Simple Filename Validation

const allowedFiles = [
  'report.pdf',
  'invoice.xlsx',
  'image.png'
];

if (!allowedFiles.includes(fileName)) {
  return res.status(404).send('Invalid file.');
}

🔄 ZIP Download Workflow

Browser
Request ZIP Archive
Browser Downloads ZIP File
Express Server
Create ZIP Archive
Add Selected Files
Stream Archive

📚 ZIP Creation Lifecycle

📊 Common Archiver Methods

MethodPurpose
archive.file()Adds a single file to the archive.
archive.directory()Adds an entire directory.
archive.append()Adds custom content or streams.
archive.pipe()Streams the archive to a writable destination.
archive.finalize()Completes archive generation.

🔍 Download Individual Files vs ZIP Archive

FeatureIndividual DownloadsZIP Archive
Number of RequestsOne request per fileSingle request
User ConvenienceLowerHigher
Download ManagementMultiple downloadsOne archive file
Network EfficiencyLess efficientMore efficient for multiple files

💡 Best Practices

  • Stream ZIP archives directly instead of storing temporary ZIP files.
  • Validate filenames before adding them to the archive.
  • Use meaningful archive names such as reports.zip or documents.zip.
  • Handle archive creation errors gracefully.
  • Protect sensitive downloads with authentication and authorization.
  • Use compression only when it improves download efficiency.

Best Practice

For large downloads, stream the ZIP archive directly to the client using archive.pipe(res). This minimizes memory usage and avoids creating unnecessary temporary files on the server.

📚 Learn More

Explore the official documentation for creating ZIP archives:
â€ĸ Express.js Official Documentation
â€ĸ Archiver Package Documentation
â€ĸ Node.js Streams Documentation

📝 Summary

Summary

Creating ZIP archives in Express.js allows users to download multiple files in a single request. By using the archiver package, you can dynamically generate ZIP files, stream them efficiently to the client, validate requested files, and provide a secure and user-friendly download experience.