Extract Zip files recursively with NodeJS
Tags: node, automation, javascript, programming
Published: 2021-05-26 16:25:59

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 

1. Find Library for extracting
2. Extract the main zip file *i.e demo.zip*
2. Figure out a way to traverse the whole folder structure recursively
3. Then extract the `.zip` file whenever it is found.


## Solution 

[**extract-zip**](https://www.npmjs.com/package/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.

```js
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

```js 
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 path

- The `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 support `async/await` keyword. Traditional for loop syntax works with `await` keyword. But I wanted to use the more common array method `map()`. 

- If you use `await` with `map()` it returns array of promises. Hence, to resolve all promises `await Promise.all(arrayOfPromises)` is used here.

- For more details on `async/await` in loops refer to [this](https://zellwk.com/blog/async-await-in-loops/) wonderful article 

```js
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 again `i.e unzipFiles()`

```js
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 with `source` and `target` 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` to `extractZip()` method.

- Now there is one more catch in the last line i.e

```js 
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.


Output will be - 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1bs2ep0azzuyms2nns41.png)

___

Thanks a lot for reading 🙏

If you enjoyed this article or found it helpful, give it a thumbs-up 👍

Feel free to connect 👋

[Twitter](https://twitter.com/saraogipraveen) | [LinkedIn](https://www.linkedin.com/in/praveensaraogi/)