Hello Coders ๐
This is my first attempt at writing a technical post. Hope you find it helpful.
Problem Statement
Recently, I encountered a requirement where I had to extract all the zip files present inside a main zip file which had a random folder structure and any of the folders can have a zip file present inside it at any level.
Breaking down of the problem
- Find Library for extracting
- Extract the main zip file i.e demo.zip
- Figure out a way to traverse the whole folder structure recursively
- Then extract the
.zip
file whenever it is found.
Solution
extract-zip library used to extract the zip files.
Method to extract zip file
- It takes two input arguments source and target. source should be absolute path of the zip file, target is where the folder will get extracted.
async function extractZip(source, target) {
try {
await extract(source, { dir: target });
console.log("Extraction complete");
} catch (err) {
console.log("Oops: extractZip failed", err);
}
}
Method to traverse folders recursively
const unzipFiles = async function (dirPath) {
const files = fs.readdirSync(dirPath);
await Promise.all(
files.map(async (file) => {
if (fs.statSync(dirPath + "/" + file).isDirectory()) {
await unzipFiles(dirPath + "/" + file);
} else {
const fullFilePath = path.join(dirPath, "/", file);
const folderName = file.replace(".zip", "");
if (file.endsWith(".zip")) {
zippedFiles.push(folderName);
await extractZip(fullFilePath, path.join(dirPath, "/", folderName));
await unzipFiles(path.join(dirPath, "/", folderName));
}
}
})
);
};
Lot of activities in above snippet. Let's decode
dirPath
: file extraction pathThe
fs.readdirSync()
method is used to synchronously read the contents of a given directory. The method returns an array with all the file names or objects in the directory.Now, the main challenge was to loop through all the folders/files asynchronously. We cannot use
forEach
since it doesn't supportasync/await
keyword. Traditional for loop syntax works withawait
keyword. But I wanted to use the more common array methodmap()
.If you use
await
withmap()
it returns array of promises. Hence, to resolve all promisesawait Promise.all(arrayOfPromises)
is used here.For more details on
async/await
in loops refer to this wonderful article
if (fs.statSync(dirPath + "/" + file).isDirectory()) {
await unzipFiles(dirPath + "/" + file);
}
- To check whether current object is file or folder
isDirectory()
method is used. If its a folder then call same method againi.e unzipFiles()
else {
const fullFilePath = path.join(dirPath, "/", file);
const folderName = file.replace(".zip", "");
if (file.endsWith(".zip")) {
zippedFiles.push(folderName);
await extractZip(fullFilePath, path.join(dirPath, "/", folderName));
await unzipFiles(path.join(dirPath, "/", folderName));
}
If a file is found then we will call the
extractZip()
method withsource
andtarget
with their absolute paths.If we don't specify the
target
, or give it a current path then it will extract all the files in current directory itself. But I wanted to extract the zip to their respective folder names.To achieve this, I have spliced the folder name from .zip file passed it as a
target
toextractZip()
method.Now there is one more catch in the last line i.e
await unzipFiles(path.join(dirPath, "/", folderName));
- Since there is a possibility that the extracted files can also have zip files inside it, so as soon as we extract any file we will again have to call
unzipFiles()
to traverse through the extracted files.
Thanks a lot for reading ๐
If you enjoyed this article or found it helpful, give it a thumbs-up ๐
Feel free to connect ๐
Top comments (2)
This is amazing ๐๐
Its great๐ค