6 Node.js Recipes – Working with the File System

 

1. Reading from a file

Problem:

You want to read a file from the file system and display its contents on the terminal screen.

Solution:

var fs = require('fs');
fs.readFile('data.txt', function (err, data) {
  if (err)
    throw err;
  if (data)
    console.log(data.toString('utf8'));
});

Discussion:

First, we include the file system core module using module inclusion as written: var fs = require(‘fs’);  allowing us to utilize all the features of the fs module using our variable we defined as fs. The next line states our input file ‘data.txt’. The next part of this line will attach our anonymous callback function function (err, data) to the actual file read operation readFile.

trans3
var fs = require('fs');
fs.writeFile('data2.txt', 'Hello, World!', function (err) {
     if (err)
       throw err;
});

Discussion:

Our anonymous callback function accepts any errors which occur as  err, and the resulting file data as a binary buffer array  data. Within the anonymous function, we first check for any potential errors and throw the error as an exception to be left to be caught by the calling process as if (err)  throw err; . The subsequent lines check to see if the data is not null, and then converts the data to a UTF-8 encoded string to be written to the console screen if (data) console.log(data.toString(‘utf8’));

It is important to note that this solution uses the asynchronous file read operation to prevent blocking. For a synchronous, or blocking file read operation, reference the Node.js documentation for readFileSync()

 

3. Working with directories

Problem:

You want to read through a list of all of the files in a given directory location and display each file’s name.

Solution:

var fs = require('fs');
fs.readdir('.', function (err, files) {
 if (err)
    throw err;
 for (var index in files) {
    console.log(files[index]);
 }
 });

Discussion:

First, we include the file system core module using module inclusion as written: var fs = require(‘fs’);  allowing us to utilize all the features of the fs module using our variable we defined as fs.  The next line states our input directory ‘.’ (the current working directory). The next part of this line will attach our anonymous callback function function (err, files) to the actual directory read operation readdir(). Our anonymous callback function accepts any errors which occur as  err and an array of filenames stored in files.

 

Within the anonymous function, we first check for any potential errors and throw the error as an exception to be left to be caught by the calling process as if (err)  throw err;. Next we iterate over each item in the filename array and display each filename on the console screen.

 

It is important to note that this solution uses the asynchronous directory read operation to prevent blocking. For a synchronous, or blocking directory reading operation, reference the Node.js documentation for readdirSync()

4. Traversing the file system

Problem:

You want to recursively read through a list of all of the files in a given directory location and display each file’s name or recursively traverse the file if it is a directory.

Solution:

 var fs = require('fs');
 var traverseFileSystem = function (currentPath) {
    console.log(currentPath);
    var files = fs.readdirSync(currentPath);
    for (var i in files) {
       var currentFile = currentPath + '/' + files[i];
       var stats = fs.statSync(currentFile);
       if (stats.isFile()) {
       console.log(currentFile);
       }
      else if (stats.isDirectory()) {
             traverseFileSystem(currentFile);
           }
     }
   };
  traverseFileSystem('..');

Discussion:

First, we include the file system core module using module inclusion as written: var fs = require(‘fs’);  allowing us to utilize all the features of the fs module using our variable we defined as fs.  The next line defines our custom function traverseFileSystem which accepts one parameter currentPath which contains the current directory path the function is traversing.

The first line of this function simply displays the current directory’s name. Next we synchronously return a list of files contained in the current directory (this is done synchronously to guarantee we have a list of files before we try to traverse them). Next we iterate over each item in the filename array. From here we create a variable to store our fully-qualified file path and obtain information about that file using a synchronous file stats call  statSync() (this function is described in more depth in the next example).

The function then checks if the current file is a file or a directory, and either displays the file’s name if it is a file or recurses by re-invoking the traverseFileSystem function using the new current directory path if it is a directory. The last line of the code invokes the  traverseFileSystem function on ‘..’ which is a simple way to reference the parent directory of the current working directory.

It is important to note that this solution uses the synchronous versions of some of the asynchronous methods used in previous examples in order to guarantee return values before proceeding with the code’s execution. This was done in the sake of simplicity in illustration of this solution and can be ported into an asynchronous version with little difficulty

5. Getting/setting file attributes

Problem:

You want to get certain attributes of a given file such as whether or not it is a file or a directory, and you want to be able to set certain attributes such as the file’s name or the file’s permissions.

Solution:

 var fs = require('fs');
 fs.stat('data.txt', function (err, stats) {
  if (err)
     throw err;
  if (stats.isFile()) {
      console.log('It\'s a file!');
  }
  if (stats.isDirectory()) {
    console.log('It\'s a directory!');
  }
  for (var i in stats) {
     if ('function' !== typeof stats[i])
     console.log(i + '\t= ' + stats[i]);
   }
  });
 fs.rename('data2.txt', 'data2_new.txt', function (err) {
  if (err)
    throw err;
  console.log('Renamed!');
 });
 fs.chmod('data3.txt', '0777', function (err) {
   if (err)
      throw err;
   console.log('File permissions changed!');
 });

Discussion:

First, we include the file system core module using module inclusion as written: var fs = require(‘fs’);  allowing us to utilize all the features of the fs module using our variable we defined as fs.

The next line obtains information about a given file ‘data.txt’ using an asynchronous file stats call  stat(). On this same line we specify our anonymous callback function function (err, stats) which takes two parameters: any errors which occur as  err and a stats object of the given file as stats. Within the anonymous function, we first check for any potential errors and throw the error as an exception to be left to be caught by the calling process as if (err)  throw err;.

 

 

transaction4

 

We then display on the console screen whether the given file is a file or a directory. After this, we step through each non-function member of the stats object and display what that member field’s name is and what value it contains (this will include detailed file information such as creation date, modified date, file size, etc).

The next section of the code executes the asynchronous rename() function to rename the file ‘data2.txt’ to ‘data2_new.txt’. In this same line, we are passing the anonymous callback function function (err) which, as before, passes any potential errors that might occur as its only parameter err.

We then use the same technique to check for any errors and then display ‘Renamed!’ if the file was successfully renamed. In the last section of the code we execute the asynchronous chmod() function to change the file permissions of our input file ‘data3.txt’ to ‘0777’ (or all read-write-execute). In this same line, we are passing the anonymous callback function function (err) which, as before, passes any potential errors that might occur as its only parameter err. We then use the same technique to check for any errors and then display ‘File permissions changed!’ if the file permissions were successfully changed.

6. Watching a file for changes

Problem:

You want to be notified of any changes that occur within a given directory either to the directory’s contents or directory itself.

Solution:

This solution requires a special module called “inotify.” Assuming you have the node package manager installed, you can install inotify using: npm install inotify The code below must then reference the install path:

 var fs = require('fs');
 var Inotify = require('/usr/local/node/node_modules/inotify').Inotify;
 var inotify = new Inotify();
 var watcher = {
 path: '.',
 watch_for: Inotify.IN_OPEN | Inotify.IN_CLOSE,
 callback: function (event) {
 var file = event.name ? event.name : '';
 var mask = event.mask;
 if (file != '' && file.indexOf('.') !== 0) {
    if (mask & Inotify.IN_OPEN) {
    console.log(file + ' was opened ');
 }
 else if (mask & Inotify.IN_CLOSE) {
    console.log(file + ' was closed ');
 }
 }
 }
 };
inotify.addWatch(watcher);

Discussion:

First, we include the file system core module using module inclusion as written: var fs = require(‘fs’);  allowing us to utilize all the features of the fs module using our variable we defined as fs. We then include the inotify module var inotify = require(‘/usr/local/node/node_modules/inotify’).Inotify;  (note that this path should be whatever path you installed the inotify module to). On the next line, we are instantiating the inotify object new Inotify(); which we will use for watching for file changes.

The next few lines are all for defining our  watcher object with the following properties: path: ‘.’ (the path to the file we want to watch—in this case, we are watching the current working directory), watch_for: Inotify.IN_OPEN | Inotify.IN_CLOSE (specifying that we will only be firing change events when a file is opened or closed), and lastly, callback: function (event) {  … } (defines our callback function that will be fired whenever the above two events are emitted). In the first line of our callback, we are getting the filename from the event object var file = event.name ? event.name : ”;  (if the filename is null, we’ll set the variable to an empty string).

The next line is just to make a “shorthand” variable for the event type mask var mask = event.mask; .  The next conditional statement will filter out empty filenames and filenames that start with a “.” (which sometimes indicate a temporary file being used to write a file). The next couple of statements if (mask & Inotify.IN_OPEN) {  … } and then after  else if (mask & Inotify.IN_CLOSE) {  … }  will display whether there was a file opened or closed under the given directory location.

Lastly is a very important line of code where we add our watcher object that we just defined to the inotify object using inotify.addWatch(watcher); (note that without this very line, our watcher object is essentially useless).

There are many more event types that you can watch files for, along with additional parameters for the watcher object. Reference the inotify github website for more information.

 

Monitis is an all-in-one hosted systems monitoring platform for small and medium-sized businesses. 70,000 sysadmins and IT managers use Monitis platform to monitor their websites, applications and networks. Monitis provides free 15-days trial.    

 

You might also like