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
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
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.
// 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)
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)
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:
- directoryA - file1 - file2 - directoryB - file1 - file2 - file3
…then the following code will create an object such as this:
{ 'directoryA' : { file1: true, file2: true }, 'directoryB' : { file1: true, file2: true, file3: true } }
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
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.)
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'