File system operations in NodeJS

Sync vs async operations

Node offers both, sync and async methods to handle files. Sync methods (having sync in the method name, e.g. fs.readFileSync()) will block the execution of the program until it has finished processing, so it is recommend to either always use async operations or at least for heavy operations, such as handling big files.

Comparing stat, lstat and fstat

  • stat follows symlinks. When given a path that is a symlink, it returns the stat of the target of the symlink.
  • lstat doesn’t follow symlinks. When given a path that is a symlink it returns the stat of the symlink and not its target.
  • fstat takes a file descriptor rather than a path.

Check if resource exists

Sync

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const fs = require('fs');
const path = require('path');
const dirname = path.join('E:', 'myFolder'');
/* Sync #1 */
fs.existsSync(dirname);
/* Sync #2 */
try {
const stat = fs.statSync(dirname);
} catch (exc) {
console.error(exc);
}
const fs = require('fs'); const path = require('path'); const dirname = path.join('E:', 'myFolder''); /* Sync #1 */ fs.existsSync(dirname); /* Sync #2 */ try { const stat = fs.statSync(dirname); } catch (exc) { console.error(exc); }
const fs = require('fs');
const path = require('path');
const dirname = path.join('E:', 'myFolder'');

/* Sync #1 */
fs.existsSync(dirname);

/* Sync #2 */
try {
    const stat = fs.statSync(dirname);
} catch (exc) {
    console.error(exc);
}

Async

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const fs = require('fs');
const path = require('path');
const dirname = path.join('E:', 'myFolder'');
/* Async #1 */
fs.access(dirname, (err, res) => {
if(err) {
console.error(err);
}
});
/* Async #2 */
fs.stat(dirname, (err, res) => {
if(err) {
console.log(err);
}
});
const fs = require('fs'); const path = require('path'); const dirname = path.join('E:', 'myFolder''); /* Async #1 */ fs.access(dirname, (err, res) => { if(err) { console.error(err); } }); /* Async #2 */ fs.stat(dirname, (err, res) => { if(err) { console.log(err); } });
const fs = require('fs');
const path = require('path');
const dirname = path.join('E:', 'myFolder'');

/* Async #1 */
fs.access(dirname, (err, res) => {
   if(err) {
       console.error(err);
   }
});

/* Async #2 */
fs.stat(dirname, (err, res) => {
    if(err) {
        console.log(err);
    }
});

Check type of existing resource

The following method will check if an existing resource is of a specific type, it will throw an error if the resource does not exist. To check for existing use fs.existsSync(dirname) (see above) instead.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// checks type of a resource that MUST exist or throws error
fs.lstatSync(fileOrDirectoryPath).isDirectory();
.isFile()
.isBlockDevice()
.isCharacterDevice()
.isSymbolicLink() // (only valid with fs.lstat())
.isFIFO()
.isSocket()
// checks type of a resource that MUST exist or throws error fs.lstatSync(fileOrDirectoryPath).isDirectory(); .isFile() .isBlockDevice() .isCharacterDevice() .isSymbolicLink() // (only valid with fs.lstat()) .isFIFO() .isSocket()
// checks type of a resource that MUST exist or throws error
fs.lstatSync(fileOrDirectoryPath).isDirectory();

.isFile()
.isBlockDevice()
.isCharacterDevice()
.isSymbolicLink() // (only valid with fs.lstat())
.isFIFO()
.isSocket()

Processing files within directories

Files within a single directory (not subdirectories)

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const fs = require('fs');
const path = require('path');
const dirname = path.join('E:', 'myFolder');
const files = fs.readdirSync(dirname)
files.forEach((file) => {
console.log(file);
})
const fs = require('fs'); const path = require('path'); const dirname = path.join('E:', 'myFolder'); const files = fs.readdirSync(dirname) files.forEach((file) => { console.log(file); })
const fs = require('fs');
const path = require('path');
const dirname = path.join('E:', 'myFolder');

const files = fs.readdirSync(dirname)

files.forEach((file) => {
    console.log(file);
})

Files including subdirectories with limited depth (sync)

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function getFiles(dirPath, currentLevel, maxLevel) {
if (currentLevel > maxLevel) {
return;
} else {
fs.readdirSync(dirPath).forEach(function (file) {
let filePath = path.join(dirPath, file);
let stat = fs.statSync(filePath);
if (stat.isDirectory()) {
console.info(`Dir: ${filePath}`);
getFiles(filePath, currentLevel + 1, maxLevel);
} else {
console.info(`File: ${filePath}`);
}
});
}
}
function getFiles(dirPath, currentLevel, maxLevel) { if (currentLevel > maxLevel) { return; } else { fs.readdirSync(dirPath).forEach(function (file) { let filePath = path.join(dirPath, file); let stat = fs.statSync(filePath); if (stat.isDirectory()) { console.info(`Dir: ${filePath}`); getFiles(filePath, currentLevel + 1, maxLevel); } else { console.info(`File: ${filePath}`); } }); } }
function getFiles(dirPath, currentLevel, maxLevel) {
    if (currentLevel > maxLevel) {
        return;
    } else {
        fs.readdirSync(dirPath).forEach(function (file) {
            let filePath = path.join(dirPath, file);
            let stat = fs.statSync(filePath);
            if (stat.isDirectory()) {
                console.info(`Dir: ${filePath}`);
                getFiles(filePath, currentLevel + 1, maxLevel);
            } else {
                console.info(`File: ${filePath}`);
            }
        });
    }
}

Building JS object tree from directory including subdirectories (async)

If you have a folder structure like this:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
- directoryA
- file1
- file2
- directoryB
- file1
- file2
- file3
- directoryA - file1 - file2 - directoryB - file1 - file2 - file3
 - directoryA
  - file1
  - file2
 - directoryB
  - file1
  - file2
  - file3

…then the following code will create an object such as this:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
{
'directoryA' : {
file1: true,
file2: true
},
'directoryB' : {
file1: true,
file2: true,
file3: true
}
}
{ 'directoryA' : { file1: true, file2: true }, 'directoryB' : { file1: true, file2: true, file3: true } }
{
  'directoryA' : {
    file1: true,
    file2: true
  },
  'directoryB' : {
    file1: true,
    file2: true,
    file3: true
  }
}
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const fs = require("fs");
const basePath = process.argv[2]; //Getting the path (it works)
const result = {};
//Function to check my path is exist and it's a directory
const isDirectory = async (path) => {
try {
const stats = await fs.promises.lstat(path);
return stats.isDirectory();
} catch (error) {
throw new Error("No such file or Directory");
}
};
//Recursive function that should create the object tree of the file system
const createTree = async (path, target) => {
const data = await fs.promises.readdir(path);
for (const item of data) {
const currentLocation = `${path}/${item}`;
const isDir = await isDirectory(currentLocation);
if (!isDir) {
target[item] = true;
continue;
}
target[item] = {};
await createTree(currentLocation, target[item]);
}
};
//Consuming the createTree function
(async () => {
try {
await createTree(basePath, result);
console.log(result);
} catch (error) {
console.log(error.message);
}
})();
const fs = require("fs"); const basePath = process.argv[2]; //Getting the path (it works) const result = {}; //Function to check my path is exist and it's a directory const isDirectory = async (path) => { try { const stats = await fs.promises.lstat(path); return stats.isDirectory(); } catch (error) { throw new Error("No such file or Directory"); } }; //Recursive function that should create the object tree of the file system const createTree = async (path, target) => { const data = await fs.promises.readdir(path); for (const item of data) { const currentLocation = `${path}/${item}`; const isDir = await isDirectory(currentLocation); if (!isDir) { target[item] = true; continue; } target[item] = {}; await createTree(currentLocation, target[item]); } }; //Consuming the createTree function (async () => { try { await createTree(basePath, result); console.log(result); } catch (error) { console.log(error.message); } })();
const fs = require("fs");

const basePath = process.argv[2]; //Getting the path (it works)
const result = {};

//Function to check my path is exist and it's a directory
const isDirectory = async (path) => {
  try {
    const stats = await fs.promises.lstat(path);
    return stats.isDirectory();
  } catch (error) {
    throw new Error("No such file or Directory");
  }
};

//Recursive function that should create the object tree of the file system
const createTree = async (path, target) => {
  const data = await fs.promises.readdir(path);
  for (const item of data) {
    const currentLocation = `${path}/${item}`;
    const isDir = await isDirectory(currentLocation);
    if (!isDir) {
      target[item] = true;
      continue;
    }
    target[item] = {};
    await createTree(currentLocation, target[item]);
  }
};

//Consuming the createTree function
(async () => {
  try {
    await createTree(basePath, result);
    console.log(result);
  } catch (error) {
    console.log(error.message);
  }
})();

Watching files in a directory

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const fs = require('fs');
const path = require('path');
const dirname = path.join('E:', 'myFolder');
fs.watch(dirname, (evt, filename) => {
if(evt === 'rename') {
console.log(`${filename} was renamed`);
}
});
const fs = require('fs'); const path = require('path'); const dirname = path.join('E:', 'myFolder'); fs.watch(dirname, (evt, filename) => { if(evt === 'rename') { console.log(`${filename} was renamed`); } });
const fs = require('fs');
const path = require('path');
const dirname = path.join('E:', 'myFolder');

fs.watch(dirname, (evt, filename) => {
    if(evt === 'rename') {
        console.log(`${filename} was renamed`);
    }
});

Get parts of a file path (file name, extension etc.)

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import Path from 'path'
// Getting the file name without extension
Path.parse('/home/user/avatar.png').name; // -> 'avatar'
// Getting just the extension
Path.parse('/home/user/avatar.png').ext; // -> 'png'
// Getting file name with extension
Path.basename('/home/user/avatar.png'); // -> 'avatar.png'
import Path from 'path' // Getting the file name without extension Path.parse('/home/user/avatar.png').name; // -> 'avatar' // Getting just the extension Path.parse('/home/user/avatar.png').ext; // -> 'png' // Getting file name with extension Path.basename('/home/user/avatar.png'); // -> 'avatar.png'
import Path from 'path'

// Getting the file name without extension
Path.parse('/home/user/avatar.png').name; // -> 'avatar'


// Getting just the extension
Path.parse('/home/user/avatar.png').ext; // -> 'png'

// Getting file name with extension
Path.basename('/home/user/avatar.png'); // -> 'avatar.png'

About Author

Mathias Bothe To my job profile

I am Mathias from Heidelberg, Germany. I am a passionate IT freelancer with 15+ years experience in programming, especially in developing web based applications for companies that range from small startups to the big players out there. I create Bosycom and initiated several software projects.