The author selected Open Sourcing Mental Illness to receive a donation as part of the Write for DOnations program.
Working with files is one of the common tasks among developers. As your files grow in size, they start taking significant space on your hard drive. Sooner or later you may need to transfer the files to other servers or upload multiple files from your local machine to different platforms. Some of these platforms have file size limits, and won’t accept large files. To get around this, you can group the files into a single ZIP file. A ZIP file is an archive format that packs and compresses files with the lossless compression algorithm. The algorithm can reconstruct the data without any data loss. In Node.js, you can use the adm-zip
module to create and read ZIP archives.
In this tutorial, you will use adm-zip
module to compress, read, and decompress files. First, you’ll combine multiple files into a ZIP archive using adm-zip
. You’ll then list the ZIP archive contents. After that, you’ll add a file to an existing ZIP archive, and then finally, you’ll extract a ZIP archive into a directory.
To follow this tutorial, you’ll need:
Node.js installed on your local or server environment. Follow How to Install Node.js and Create a Local Development Environment to install Node.js.
Knowledge of how to write a Node.js program, see How To Write and Run Your First Program in Node.js.
A basic understanding of asynchronous programming in JavaScript. Visit our tutorial Understanding the Event Loop, Callbacks, Promises, and Async/Await in JavaScript to learn the basics.
Knowledge of how to work with files in Node.js. See the tutorial How To Work with Files using the fs Module in Node.js to review working with files.
In this step, you’ll create the directory for your project and install adm-zip
as a dependency. This directory is where you’ll keep your program files. You’ll also create another directory containing text files and an image. You’ll archive this directory in the next section.
Create a directory called zip_app
with the following command:
- mkdir zip_app
Navigate into the newly created directory with the cd
command:
- cd zip_app
Inside the directory, create a package.json
file to manage the project dependencies:
- npm init -y
The -y
option creates a default package.json
file.
Next, install adm-zip
with the npm install
command:
- npm install adm-zip
After you run the command, npm
will install adm-zip
and update the package.json
file.
Next, create a directory called test
and move into it:
- mkdir test && cd test
In this directory, you will create three text files and download an image. The three files will be filled with dummy content to make their file sizes larger. This will help to demonstrate ZIP compression when you archive this directory.
Create the file1.txt
and fill it with dummy content using the following command:
- yes "dummy content" | head -n 100000 > file1.txt
The yes
command logs the string dummy content
repeatedly. Using the pipe command |
, you send the output from the yes
command to be used as input for the head
command. The head
command prints part of the given input into the standard output. The -n
option specifies the number of lines that should be written to the standard output. Finally, you redirect the head
output to a new file file1.txt
using >
.
Create a second file with the string “dummy content” repeated 300,000 lines:
- yes "dummy content" | head -n 300000 > file2.txt
Create another file with the dummy content
string repeated 600,000 lines:
- yes "dummy content" | head -n 600000 > file3.txt
Finally, download an image into the directory using curl
:
- curl -O https://assets.digitalocean.com/how-to-process-images-in-node-js-with-sharp/underwater.png
Move back into the main project directory with the following command:
- cd ..
The ..
will move you to the parent directory, which is zip_app
.
You’ve now created the project directory, installed adm-zip
, and created a directory with files for archiving. In the next step, you’ll archive a directory using the adm-zip
module.
In this step, you’ll use adm-zip
to compress and archive the directory you created in the previous section.
To archive the directory, you’ll import the adm-zip
module and use the module’s addLocalFolder()
method to add the directory to the adm-zip
module’s ZIP object. Afterward, you’ll use the module’s writeZip()
method to save the archive in your local system.
Create and open a new file createArchive.js
in your preferred text editor. This tutorial uses nano
, a command-line text editor:
- nano createArchive.js
Next, require in the adm-zip
module in your createArchive.js
file:
const AdmZip = require("adm-zip");
The adm-zip
module provides a class that contains methods for creating ZIP archives.
Since it’s common to encounter large files during the archiving process, you might end up blocking the main thread until the ZIP archive is saved. To write non-blocking code, you’ll define an asynchronous function to create and save a ZIP archive.
In your createArchive.js
file, add the following highlighted code:
const AdmZip = require("adm-zip");
async function createZipArchive() {
const zip = new AdmZip();
const outputFile = "test.zip";
zip.addLocalFolder("./test");
zip.writeZip(outputFile);
console.log(`Created ${outputFile} successfully`);
}
createZipArchive();
createZipArchive
is an asynchronous function that creates a ZIP archive from a given directory. What makes it asynchronous is the async
keyword you defined before the function label. Within the function, you create an instance of the adm-zip
module, which provides methods you can use for reading and creating archives. When you create an instance, adm-zip
creates an in-memory ZIP where you can add files or directories.
Next, you define the archive name and store it in the outputDir
variable. To add the test
directory to the in-memory archive, you invoke the addLocalFolder()
method from adm-zip
with the directory path as an argument.
After the directory is added, you invoke the writeZip()
method from adm-zip
with a variable containing the name of the ZIP archive. The writeZip()
method saves the archive to your local disk.
Once that’s done, you invoke console.log()
to log that the ZIP file has been created successfully.
Finally, you call the createZipArchive()
function.
Before you run the file, wrap the code in a try…catch block to handle runtime errors:
const AdmZip = require("adm-zip");
async function createZipArchive() {
try {
const zip = new AdmZip();
const outputFile = "test.zip";
zip.addLocalFolder("./test");
zip.writeZip(outputFile);
console.log(`Created ${outputFile} successfully`);
} catch (e) {
console.log(`Something went wrong. ${e}`);
}
}
createZipArchive();
Within the try
block, the code will attempt to create a ZIP archive. If successful, the createZipArchive()
function will exit, skipping the catch
block. If creating a ZIP archive triggers an error, execution will skip to the catch
block and log the error in the console.
Save and exit the file in nano
with CTRL+X
. Enter y
to save the changes, and confirm the file by pressing ENTER
on Windows, or the RETURN
key on the Mac.
Run the createArchive.js
file using the node
command:
- node createArchive.js
You’ll receive the following output:
OutputCreated test.zip successfully
List the directory contents to see if the ZIP archive has been created:
- ls
You’ll receive the following output showing the archive among the contents:
OutputcreateArchive.js node_modules package-lock.json
package.json test test.zip
With the confirmation that the ZIP archive has been created, you’ll compare the ZIP archive, and the test
directory file size to see if the compression works.
Check the test
directory size using the du
command:
- du -h test
The -h
flag instructs du
to show the directory size in a human-readable format.
After running the command, you will receive the following output:
Output15M test
Next, check the test.zip
archive file size:
- du -h test.zip
The du
command logs the following output:
Output760K test.zip
As you can see, creating the ZIP file has dropped the directory size from 15 Megabytes(MB) to 760 Kilobytes(KB), which is a huge difference. The ZIP file is more portable and smaller in size.
Now that you created a ZIP archive, you’re ready to list the contents in a ZIP file.
In this step, you’ll read and list all files in a ZIP archive using adm-zip
. To do that, you’ll instantiate the adm-zip
module with your ZIP archive path. You’ll then call the module’s getEntries()
method which returns an array of objects. Each object holds important information about an item in the ZIP archive. To list the files, you’ll iterate over the array and access the filename from the object, and log it in the console.
Create and open readArchive.js
in your favorite text editor:
- nano readArchive.js
In your readArchive.js
, add the following code to read and list contents in a ZIP archive:
const AdmZip = require("adm-zip");
async function readZipArchive(filepath) {
try {
const zip = new AdmZip(filepath);
for (const zipEntry of zip.getEntries()) {
console.log(zipEntry.toString());
}
} catch (e) {
console.log(`Something went wrong. ${e}`);
}
}
readZipArchive("./test.zip");
First, you require in the adm-zip
module.
Next, you define the readZipArchive()
function, which is an asynchronous function. Within the function, you create an instance of adm-zip
with the path of the ZIP file you want to read. The file path is provided by the filepath
parameter. adm-zip
will read the file and parse it.
After reading the archive, you define a for....of
statement that iterates over objects in an array that the getEntries()
method from adm-zip
returns when invoked. On each iteration, the object is assigned to the zipEntry
variable. Inside the loop, you convert the object into a string that represents the object using the Node.js toString()
method, then log it in the console using the console.log()
method.
Finally, you invoke the readZipArchive()
function with the ZIP archive file path as an argument.
Save and exit your file, then run the file with the following command:
- node readArchive.js
You will get output that resembles the following(edited for brevity):
Output{
"entryName": "file1.txt",
"name": "file1.txt",
"comment": "",
"isDirectory": false,
"header": {
...
},
"compressedData": "<27547 bytes buffer>",
"data": "<null>"
}
...
The console will log four objects. The other objects have been edited out to keep the tutorial brief.
Each file in the archive is represented with an object similar to the one in the preceding output. To get the filename for each file, you need to access the name
property.
In your readArchive.js
file, add the following highlighted code to access each filename:
const AdmZip = require("adm-zip");
async function readZipArchive(filepath) {
try {
const zip = new AdmZip(filepath);
for (const zipEntry of zip.getEntries()) {
console.log(zipEntry.name);
}
} catch (e) {
console.log(`Something went wrong. ${e}`);
}
}
readZipArchive("./test.zip");
Save and exit your text editor. Now, run the file again with the node
command:
- node readArchive.js
Running the file results in the following output:
Outputfile1.txt
file2.txt
file3.txt
underwater.png
The output now logs the filename of each file in the ZIP archive.
You can now read and list each file in a ZIP archive. In the next section, you’ll add a file to an existing ZIP archive.
In this step, you’ll create a file and add it to the ZIP archive you created earlier without extracting it. First, you’ll read the ZIP archive by creating an adm-zip
instance. Second, you’ll invoke the module’s addFile()
method to add the file in the ZIP. Finally, you’ll save the ZIP archive in the local system.
Create another file file4.txt
with dummy content repeated 600,000 lines:
- yes "dummy content" | head -n 600000 > file4.txt
Create and open updateArchive.js
in your text editor:
- nano updateArchive.js
Require in the adm-zip
module and the fs
module that allows you to work with files in your updateArchive.js
file:
const AdmZip = require("adm-zip");
const fs = require("fs").promises;
You require in the promise-based version of the fs
module version, which allows you to write asynchronous code. When you invoke an fs
method, it will return a promise.
Next in your updateArchive.js
file, add the following highlighted code to add a new file to the ZIP archive:
const AdmZip = require("adm-zip");
const fs = require("fs").promises;
async function updateZipArchive(filepath) {
try {
const zip = new AdmZip(filepath);
content = await fs.readFile("./file4.txt");
zip.addFile("file4.txt", content);
zip.writeZip(filepath);
console.log(`Updated ${filepath} successfully`);
} catch (e) {
console.log(`Something went wrong. ${e}`);
}
}
updateZipArchive("./test.zip");
updateZipArchive
is an asynchronous function that reads a file in the filesystem and adds it to an existing ZIP. In the function, you create an instance of adm-zip
with the ZIP archive file path in the filepath
as a parameter. Next, you invoke the fs
module’s readFile()
method to read the file in the file system. The readFile()
method returns a promise, which you resolve with the await
keyword (await
is valid in only asynchronous functions). Once resolved, the method returns a buffer object, which contains the file contents.
Next, you invoke the addFile()
method from adm-zip
. The method takes two arguments. The first argument is the filename you want to add to the archive, and the second argument is the buffer object containing the contents of the file that the readFile()
method reads.
Afterwards, you invoke adm-zip
module’s writeZip()
method to save and write new changes in the ZIP archive. Once that’s done, you call the console.log()
method to log a success message.
Finally, you invoke the updateZipArchive()
function with the Zip archive file path as an argument.
Save and exit your file. Run the updateArchive.js
file with the following command:
- node updateArchive.js
You’ll see output like this:
OutputUpdated ./test.zip successfully
Now, confirm that the ZIP archive contains the new file. Run the readArchive.js
file to list the contents in the ZIP archive with the following command:
- node readArchive.js
You’ll receive the following output:
file1.txt
file2.txt
file3.txt
file4.txt
underwater.png
This confirms that the file has been added to the ZIP.
Now that you can add a file to an existing archive, you’ll extract the archive in the next section.
In this step, you’ll read and extract all contents in a ZIP archive into a directory. To extract a ZIP archive, you’ll instantiate adm-zip
with the archive file path. After that, you’ll invoke the module’s extractAllTo()
method with the directory name you want your extracted ZIP contents to reside.
Create and open extractArchive.js
in your text editor:
- nano extractArchive.js
Require in the adm-zip
module and the path
module in your extractArchive.js
file:
const AdmZip = require("adm-zip");
const path = require("path");
The path
module provides helpful methods for dealing with file paths.
Still in your extractArchive.js
file, add the following highlighted code to extract an archive:
const AdmZip = require("adm-zip");
const path = require("path");
async function extractArchive(filepath) {
try {
const zip = new AdmZip(filepath);
const outputDir = `${path.parse(filepath).name}_extracted`;
zip.extractAllTo(outputDir);
console.log(`Extracted to "${outputDir}" successfully`);
} catch (e) {
console.log(`Something went wrong. ${e}`);
}
}
extractArchive("./test.zip");
extractArchive()
is an asynchronous function that takes a parameter containing the file path of the ZIP archive. Within the function, you instantiate adm-zip
with the ZIP archive file path provided by the filepath
parameter.
Next, you define a template literal. Inside the template literal placeholder (${}
), you invoke the parse()
method from the path
module with the file path. The parse()
method returns an object. To get the name of the ZIP file without the file extension, you append the name
property to the object that the parse()
method returns. Once the archive name is returned, the template literal interpolates the value with the _extracted
string. The value is then stored in the outputDir
variable. This will be the name of the extracted directory.
Next, you invoke adm-zip
module’s extractAllTo
method with the directory name stored in the outputDir
to extract the contents in the directory. After that, you invoke console.log()
to log a success message.
Finally, you call the extractArchive()
function with the ZIP archive path.
Save your file and exit the editor, then run the extractArchive.js
file with the following command:
- node extractArchive.js
You receive the following output:
OutputExtracted to "test_extracted" successfully
Confirm that the directory containing the ZIP contents has been created:
- ls
You will receive the following output:
OutputcreateArchive.js file4.txt package-lock.json
readArchive.js test.zip updateArchive.js
extractArchive.js node_modules package.json
test test_extracted
Now, navigate into the directory containing the extracted contents:
- cd test_extracted
List the contents in the directory:
- ls
You will receive the following output:
Outputfile1.txt file2.txt file3.txt file4.txt underwater.png
You can now see that the directory has all the files that were in the original directory.
You’ve now extracted the ZIP archive contents into a directory.
In this tutorial, you created a ZIP archive, listed its contents, added a new file to the archive, and extracted all of its content into a directory using adm-zip
module. This will serve as a good foundation for working with ZIP archives in Node.js.
To learn more about adm-zip
module, view the adm-zip documentation. To continue building your Node.js knowledge, see How To Code in Node.js series
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Hi! thank you so much for this post! I would only change this line:
for this:
So your unzipped folder will always be in the same directory level of the file that you’re unzipping.
Cheers!