Build a Simple File Compressor with Node.js in 10 Minutes

Abigeal AfolabiAbigeal Afolabi
5 min read

Introduction

Have you ever wondered how file compression works behind the scenes? In this beginner-friendly tutorial, I'll show you how to build a simple command-line tool with Node.js that can compress and decompress files in just 10 minutes! We'll explore Node's powerful streams without getting overwhelmed by complex details.

What You'll Need

  • Node.js is installed on your computer

  • Basic JavaScript knowledge

  • A text editor or IDE

  • 10 minutes of your time!

Project Setup

First, let's create a new folder and set up our project:

mkdir file-compressor
cd file-compressor
npm init -y
npm install readline-sync

We're using just one external package readline-sync to make it easier to get user input from the command line.

The Code: Step by Step

Let's build our file compressor piece by piece:

Step 1: Import the Dependencies

Create a file called index.js and add these imports:

// Import the modules we need
const fs = require('fs');          // For working with files
const zlib = require('zlib');      // For compression
const path = require('path');      // For handling file paths
const readlineSync = require('readline-sync'); // For user input

// Show a welcome message
console.log('==== File Compressor ====');

Step 2: Create a Menu Interface

// Ask the user what they want to do
const choice = readlineSync.question('Do you want to (c)ompress or (d)ecompress a file? ');

if (choice.toLowerCase() === 'c') {
  compressFile();
} else if (choice.toLowerCase() === 'd') {
  decompressFile();
} else {
  console.log('Invalid choice. Please enter "c" or "d".');
}

Step 3: Implement the Compression Function

function compressFile() {
  // Ask for the file to compress
  const inputPath = readlineSync.question('Enter the path of the file to compress: ');

  // Check if the file exists
  if (!fs.existsSync(inputPath)) {
    console.log('That file doesn\'t exist. Please check the path and try again.');
    return;
  }

  // Create output filename
  const outputPath = inputPath + '.gz';

  console.log('Starting compression...');

  // Set up our file processing pipeline
  const readStream = fs.createReadStream(inputPath);
  const writeStream = fs.createWriteStream(outputPath);
  const gzip = zlib.createGzip();

  // Connect everything together
  readStream
    .pipe(gzip)
    .pipe(writeStream)
    .on('finish', () => {
      // Show results when done
      const originalSize = fs.statSync(inputPath).size;
      const compressedSize = fs.statSync(outputPath).size;
      const percentage = ((1 - compressedSize / originalSize) * 100).toFixed(2);

      console.log('\nCompression complete!');
      console.log(`Original size: ${formatSize(originalSize)}`);
      console.log(`Compressed size: ${formatSize(compressedSize)}`);
      console.log(`You saved: ${percentage}% of space`);
    })
    .on('error', (err) => {
      console.log('Something went wrong:', err.message);
    });
}

Step 4: Implement the Decompression Function

function decompressFile() {
  // Ask for the file to decompress
  const inputPath = readlineSync.question('Enter the path of the compressed file: ');

  // Check if the file exists
  if (!fs.existsSync(inputPath)) {
    console.log('That file doesn\'t exist. Please check the path and try again.');
    return;
  }

  // Create output filename (remove .gz if present)
  let outputPath = inputPath.endsWith('.gz') 
    ? inputPath.slice(0, -3) 
    : inputPath + '.decompressed';

  console.log('Starting decompression...');

  // Set up our file processing pipeline
  const readStream = fs.createReadStream(inputPath);
  const writeStream = fs.createWriteStream(outputPath);
  const gunzip = zlib.createGunzip();

  // Connect everything together
  readStream
    .pipe(gunzip)
    .pipe(writeStream)
    .on('finish', () => {
      console.log('\nDecompression complete!');
      console.log(`Output file: ${outputPath}`);
    })
    .on('error', (err) => {
      console.log('Something went wrong:', err.message);
      console.log('This might not be a valid compressed file.');
    });
}

Step 5: Add a Helper Function for Formatting File Sizes

// Helper function to format file sizes
function formatSize(bytes) {
  if (bytes < 1024) {
    return bytes + ' bytes';
  } else if (bytes < 1024 * 1024) {
    return (bytes / 1024).toFixed(2) + ' KB';
  } else {
    return (bytes / (1024 * 1024)).toFixed(2) + ' MB';
  }
}

How to Run Your File Compressor

Save your code and run it from the terminal with:

node index.js

Follow the prompts to compress or decompress a file. Try compressing a text file first - you'll be amazed at how much smaller it gets!

Understanding Streams: The Key Concept

The magic of this program is in this simple line:

readStream.pipe(gzip).pipe(writeStream)

This creates a pipeline where:

  1. readStream reads your file bit by bit

  2. Each bit flows through gzip which compresses it

  3. The compressed bits flow to writeStream which saves them

Think of it like a water pipe system. The beauty is that you can process files of any size because you're only handling small pieces at a time. This is why Node.js is so powerful for file operations!

Troubleshooting Common Issues

  • "File not found" error: Make sure you type the full path to your file

  • Nothing happens after starting compression: Large files take time to process, be patient

  • The decompressed file doesn't work: Make sure you're decompressing a proper gzip file

  • Error messages during decompression: The input file may not be a valid gzip file

What You've Learned

In just a few minutes, you've:

  • Created a useful command-line tool

  • Learned about Node.js streams

  • Used the built-in compression library

  • Processed files efficiently

Now you understand one of the most powerful patterns in Node.js! This same approach works for many other types of file processing tasks.

Next Steps to Enhance Your Compressor

Once you're comfortable with this code, try:

  • Adding a progress indicator

  • Compressing entire folders

  • Supporting different compression levels

  • Creating a simple GUI interface with Electron

Conclusion

File compression is a fascinating topic, and Node.js makes it remarkably straightforward to implement. The streams API is one of Node's most powerful features, allowing you to process data efficiently without loading everything into memory at once.

I hope you enjoyed building this simple file compressor! Let me know in the comments if you have any questions or ideas for improving it.


If you found this tutorial helpful, don't forget to like and share it with fellow developers! Also, follow me for more Node.js tutorials and web development content.

Happy coding!

0
Subscribe to my newsletter

Read articles from Abigeal Afolabi directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Abigeal Afolabi
Abigeal Afolabi

๐Ÿš€ Software Engineer by day, SRE magician by night! โœจ Tech enthusiast with an insatiable curiosity for data. ๐Ÿ“ Harvard CS50 Undergrad igniting my passion for code. Currently delving into the MERN stack โ€“ because who doesn't love crafting seamless experiences from front to back? Join me on this exhilarating journey of embracing technology, penning insightful tech chronicles, and unraveling the mysteries of data! ๐Ÿ”๐Ÿ”ง Let's build, let's write, let's explore โ€“ all aboard the tech express! ๐Ÿš‚๐ŸŒŸ #CodeAndCuriosity