← coderrocketfuel.com

Get the Total Size of All Files in a Directory Using Node.js

How do you get the total size of all the files in a directory and any sub-directories using Node.js?

You can do this by using both the Fs core module and some custom code to recursively go through each directory (and sub-directories) to retrieve each individual file. Once you have each file, you can easily get the sum of each of their file sizes.

In this article, we'll walk you through each of the three steps to do this:

  1. Get all the files in the directory by recursively going through all the sub-directories and returning an array of file paths (the fs module will be utilized for this step).
  2. Loop through each file in the array of file paths and generate a sum of all their individual file sizes.
  3. Create a function that transforms the bytes result from the previous step into a human-readable value in KB, MB, GB, or TB units.

For the full code solution, you can jump to the end of the article.

To follow along with the code in this article, you'll need to have Node.js installed on your local development machine.

If you need one, we wrote a guide on how to get Node.js installed on your machine.

Let's get started!

Table of Contents

Step 1 - Get All the Files in the Directory

The first thing we need to do is get all the files in the directory. And this includes recursively going through to get any files in sub-directories.

To do this, we'll create a function that takes a directory path as a parameter and returns an array of file paths as a result.

If the function encounters any sub-directories, it will recursively call itself. It will do this as many times as necessary until no more sub-directories are encountered.

Here's what the code looks like:

const fs = require("fs")
const path = require("path")

const getAllFiles = function(dirPath, arrayOfFiles) {
  files = fs.readdirSync(dirPath)

  arrayOfFiles = arrayOfFiles || []

  files.forEach(function(file) {
    if (fs.statSync(dirPath + "/" + file).isDirectory()) {
      arrayOfFiles = getAllFiles(dirPath + "/" + file, arrayOfFiles)
    } else {
      arrayOfFiles.push(path.join(__dirname, dirPath, file))
    }
  })

  return arrayOfFiles
}

Let's go over each part of the code.

The getAllFiles variable holds the recursive function that will go through each subdirectory and return an array of filenames. It takes a directory file path and an optional arrayOfFiles as arguments.

Inside the getAllFiles function, we first use the readdirSync() function to get all of the files and directories inside the given dirPath supplied to the function.

Then, we create an arrayOfFiles that will hold all the filenames that will be returned when the function is done running.

Next, we loop over each item (file or directory) found by the readdirSync() function. If the item is a directory, we have the function recursively call itself to get all of the files and sub-directories inside the given directory.

And if the item is a file, we simply append the filename to the arrayOfFiles array.

When the forEach loop has finished, we return the arrayOfFiles array which contains all the files in the directory.

This function can be used like this and will return an array of file path strings:

const result = getAllFiles("PATH_TO_PARENT_DIRECTORY")
// [ "FILE_PATH", "FILE_PATH", "FILE_PATH" ]

To avoid that, you can replace the fs.readdirSync() method in the function with it's fs.readdir() synchronous counterpart.

Now that we know how to get all the files in the directory, we can add the file size of each one together in the next step.

Step 2 - Add Up the Combined Byte Size of Each File

Now, let's create a function that takes a directory path, runs the getAllFiles function we created early, and loops through each item in that array and adds together each file size value.

Add this function in the same file as you used in the last example:

const fs = require("fs")

const getTotalSize = function(directoryPath) {
  const arrayOfFiles = getAllFiles(directoryPath)

  let totalSize = 0

  arrayOfFiles.forEach(function(filePath) {
    totalSize += fs.statSync(filePath).size
  })

  return totalSize
}

We name our function getTotalSize and configure it to take a directory path as its sole parameter.

Inside the function, the first thing we do is get the array of file paths using the getAllFiles function we created in the last section.

Then, we use the forEach() method to loop over each item in the array and increment the totalSize value by the file size of each item in the array (in bytes).

When the loop is complete, the totalSize value is returned. That value will be a large number in the bytes format.

You can call the function like this:

const result = getTotalSize("./directory-name")
// 163753

The integer returned is a bytes value because the fs module only reads file sizes in those unit types. Therefore, the returned number doesn't mean much to humans.

In the next function, we'll build a quick function that formats that bytes value nicely and makes it human-readable.

Step 3 - Convert Bytes to Human-Readable Format

At this point, we have a bytes value that represents the total combined size of every file in the directory. But, what's the point of all that work we did in the previous sections if no one can understand the data we retrieved.

In this section, we'll build a function that takes a bytes value as a parameter and converts it into a formatted and human-readable KB, MB, GB, or TB version.

Here's the code:

const convertBytes = function(bytes) {
  const sizes = ["Bytes", "KB", "MB", "GB", "TB"]

  if (bytes == 0) {
    return "n/a"
  }

  const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)))

  if (i == 0) {
    return bytes + " " + sizes[i]
  }

  return (bytes / Math.pow(1024, i)).toFixed(1) + " " + sizes[i]
}

Before going through the code, it's worth noting that we use 1024 as the base unit for all conversions. We use that number because a kilobyte represents 1024 bytes in the binary system. And a megabyte is 1024 kilobytes and so on.

The first thing we do is create a function named convertBytes() that takes a bytes integer as its sole argument.

Inside the function, we create a sizes array of strings. This will store each of the potential labels (Bytes, Kilobyte, Megabyte, Gigabyte, Terabyte) that we'll use when creating a new string value representing our original bytes parameter value. Each of these will be accessed by their index value later on in the function.

Then, we create an if statement that returns the string "n/a" if the bytes parameter value is equal to zero.

Next, we need to figure out what file type we need to use from the sizes array. The variable named i will represent the index of that string in the sizes array. That value is determined by dividing the log of bytes value by the log of 1024 (file sizes are based on those units).

If the i index value is equal to 0, we'll return a string with the "bytes" label on it.

Otherwise, we'll return a string with the formatted bytes value to one decimal point. And the file type from the sizes array will be added to the end of the string as well.

Here are some examples of the function in practice:

convertBytes(0)              // "n/a"
convertBytes(500)            // "500 Bytes"
convertBytes(2000)           // "1.0 KB"
convertBytes(2024000)        // "1.9 MB"
convertBytes(2550024000)     // "2.4 GB"
convertBytes(1292550024000)  // "1.2 TB"

As you can see, the function works as expected in providing a given bytes integer into a human-readable format.

Make sure you add the convertBytes function to the same file as the other functions and you can use it when you return the final totalSize value:

return convertBytes(totalSize)

When you run the function, you'll notice that the final value is no longer in bytes, but a nicely formatted and human-readable format instead.

In this article, we've covered how to get the total size of all files in a given directory. We've done it by first getting an array of all the files in the directory and its sub-directories, then we added all of the file sizes together, and formatted the bytes into something human-readable.

For your reference, here's the full code:

const fs = require("fs")
const path = require("path")

const getAllFiles = function(dirPath, arrayOfFiles) {
  files = fs.readdirSync(dirPath)

  arrayOfFiles = arrayOfFiles || []

  files.forEach(function(file) {
    if (fs.statSync(dirPath + "/" + file).isDirectory()) {
      arrayOfFiles = getAllFiles(dirPath + "/" + file, arrayOfFiles)
    } else {
      arrayOfFiles.push(path.join(__dirname, dirPath, file))
    }
  })

  return arrayOfFiles
}

const convertBytes = function(bytes) {
  const sizes = ["Bytes", "KB", "MB", "GB", "TB"]

  if (bytes == 0) {
    return "n/a"
  }

  const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)))

  if (i == 0) {
    return bytes + " " + sizes[i]
  }

  return (bytes / Math.pow(1024, i)).toFixed(1) + " " + sizes[i]
}

const getTotalSize = function(directoryPath) {
  const arrayOfFiles = getAllFiles(directoryPath)

  let totalSize = 0

  arrayOfFiles.forEach(function(filePath) {
    totalSize += fs.statSync(filePath).size
  })

  return convertBytes(totalSize)
}

const result = getTotalSize("./my-directory")
// 159.9 KB

If you don't want to deal with creating this custom code, there is a NPM package named get-folder-size that will handle this functionality for you. Hopefully, this was helpful in your coding endeavors.