6
Accessing the File System from Node.js

Interacting with the file system in Node.js is important especially if you need to manage dynamic files to support a web application or service. Node.js provides a good interface for interacting with the file system in the fs module. This module provides the standard file access APIs that are available in most languages to open, read, write, and interact with files.

This chapter provides you with the fundamentals necessary to access the file system from Node.js applications. You should come away with the ability to create, read, and modify files as well as navigate the directory structure. You also learn how to access file and folder information as well as delete, truncate, and rename files and folders.

For all the file system calls discussed in this chapter, you need to have loaded the fs module, for example:

var fs  = require('fs');

Synchronous Versus Asynchronous File System Calls

The fs module provided in Node.js makes almost all functionality available in two forms: asynchronous and synchronous. For example, there is the asynchronous form write() and the synchronous form writeSync(). It is important to understand the difference when you are implementing your code.

Synchronous file system calls block until the call completes and then control is released back to the thread. This has advantages but can also cause severe performance issues in Node.js if synchronous calls block the main event thread or too many of the background thread pool threads. Therefore, synchronous file system calls should be limited in use when possible.

Asynchronous calls are placed on the event queue to be run later. This allows the calls to fit into the Node.js event model; however, this can be tricky when executing your code because the calling thread continues to run before the asynchronous call gets picked up by the event loop.

For the most part, the underlying functionality of both synchronous and asynchronous file system calls is exactly the same. They both accept the same parameters with the exception that all asynchronous calls require an extra parameter at the end, which is a callback function to execute when the file system call completes.

The following list describes the important differences between synchronous and asynchronous file system calls in Node.js:

Images Asynchronous calls require a callback function as an extra parameter. The callback function is executed when the file system request completes, and typically contains an error as its first parameter.

Images Exceptions are automatically handled by asynchronous calls, and an error object is passed as the first parameter if an exception occurs. Exceptions in synchronous calls must be handled by your own try/catch blocks of code.

Images Synchronous calls are run immediately, and execution does not return to the current thread until they are complete. Asynchronous calls are placed on the event queue, and execution returns to the running thread code, but the actual call will not execute until picked up by the event loop.

Opening and Closing Files

Node provides synchronous and asynchronous methods for opening files. Once a file is opened, you can read data from it or write data to it depending on the flags used to open the file. To open files in a Node.js app, use one of the following statements for asynchronous or synchronous:

fs.open(path, flags, [mode], callback)
fs.openSync(path, flags, [mode])

The path parameter specifies a standard path string for your file system. The flags parameter specifies what mode to open the file in—read, write, append, and so on—as described in Table 6.1. The optional mode parameter sets the file access mode and defaults to 0666, which is readable and writable.

Table 6.1 Flags that define how files are opened

Mode

Description

r

Open file for reading. An exception occurs if the file does not exist.

r+

Open file for reading and writing. An exception occurs if the file does not exist.

rs

Open file for reading in synchronous mode. This is not the same as forcing fs.openSync(). When used, the OS bypasses the local file system cache. Useful on NFS mounts because it lets you skip the potentially stale local cache. You should only use this flag if necessary because it can have a negative impact on performance.

rs+

Same as rs except the file is open file for reading and writing.

w

Open file for writing. The file is created if it does not exist or truncated if it does exist.

wx

Same as w but fails if the path exists.

w+

Open file for reading and writing. The file is created if it does not exist or truncated if it exists.

wx+

Same as w+ but fails if path exists.

a

Open file for appending. The file is created if it does not exist.

ax

Same as a but fails if the path exists.

a+

Open file for reading and appending. The file is created if it does not exist.

ax+

Same as a+ but fails if the path exists.

Once a file has been opened, you need to close it to force flushing changes to disk and release the OS lock. Closing a file is done using one of the following methods and passing the file handle to it. In the case of the asynchronous close() call, you also need to specify a callback function:

fs.close(fd, callback)
fs.closeSync(fd)

The following shows an example of opening and closing a file in asynchronous mode. Notice that a callback function is specified that receives an err and an fd parameter. The fd parameter is the file descriptor that you can use to read or write to the file:

fs.open("myFile", 'w', function(err, fd){
  if (!err){
    fs.close(fd);
  }
});

The following shows an example of opening and closing a file in synchronous mode. Notice that a there is no callback function and that the file descriptor used to read and write to the file is returned directly from fs.openSync():

var fd = fs.openSync("myFile", 'w');
fs.closeSync(fd);

Writing Files

The fs module provides four different ways to write data to files. You can write data to a file in a single call, write chunks using synchronous writes, write chunks using asynchronous writes, or stream writes through a Writable stream. Each of these methods accepts either a String or a Buffer object as input. The following sections describe how to use these methods.

Simple File Write

The simplest method for writing data to a file is to use one of the writeFile() methods. These methods write the full contents of a String or Buffer to a file. The following shows the syntax for the writeFile() methods:

fs.writeFile(path, data, [options], callback)
fs.writeFileSync(path, data, [options])

The path parameter specifies the path to the file. The path can be relative or absolute. The data parameter specifies the String or Buffer object to be written to the file. The optional options parameter is an object that can contain encoding, mode, and flag properties that define the string encoding as well as the mode and flags used when opening the file. The asynchronous method also requires a callback that is called when the file write has been completed.

Listing 6.1 implements a simple asynchronous fileWrite() request to store a JSON string of a config object in a file. Listing 6.1 Output shows the output of the code.

Listing 6.1 file_write.js: Writing a JSON string to a file

01 var fs = require('fs');
02 var config = {
03   maxFiles: 20,
04   maxConnections: 15,
05   rootPath: "/webroot"
06 };
07 var configTxt = JSON.stringify(config);
08 var options = {encoding:'utf8', flag:'w'};
09 fs.writeFile('config.txt', configTxt, options, function(err){
10   if (err){
11     console.log("Config Write Failed.");
12   } else {
13     console.log("Config Saved.");
14   }
15 });

Listing 6.1 Output file_write.js: Writing a configuration file

C:ooks
odech06writing>node file_write.js
Config Saved.

Synchronous File Writing

The synchronous method of file writing writes the data to the file before returning execution to the running thread. This provides the advantage of allowing you to write multiple times in the same section of code, but this can be a disadvantage if the file writes hold up other threads as discussed earlier.

To write to a file synchronously, first open it using openSync() to get a file descriptor and then use fs.writeSync() to write data to the file. The following shows the syntax for fs.writeSync():

fs.writeSync(fd, data, offset, length, position)

The fd parameter is the file descriptor returned by openSync(). The data parameter specifies the String or Buffer object to be written to the file. The offset parameter specifies the index in the input data to begin reading from; if you want to begin at the current index in the String or Buffer, this value should be null. The length specifies the number of bytes to write; specifying null writes until the end of the data buffer. The position argument specifies the position in the file to begin writing at; specifying null for this value uses the current file position.

Listing 6.2 illustrates implementing basic synchronous writing to store a series of string data in a file. Listing 6.2 Output shows the result.

Listing 6.2 file_write_sync.js: Performing synchronous writes to a file

1 var fs = require('fs');
2 var veggieTray = ['carrots', 'celery', 'olives'];
3 fd = fs.openSync('veggie.txt', 'w');
4 while (veggieTray.length){
5   veggie = veggieTray.pop() + " ";
6   var bytes = fs.writeSync(fd, veggie, null, null);
7   console.log("Wrote %s %dbytes", veggie, bytes);
8 }
9 fs.closeSync(fd);

Listing 6.2 Output file_write_sync.js: Writing synchronously to a file

C:ooks
odech06writing>node file_write_sync.js
Wrote olives  7bytes
Wrote celery  7bytes
Wrote carrots  8bytes

Asynchronous File Writing

The asynchronous method of file writing puts the write request on the event queue and then returns control back to the calling code. The actual write does not take place until the event loop picks up the write request and executes it. You need to be careful when performing multiple asynchronous write requests on the same file, since you cannot guarantee what order they will be executed unless you wait for the first write callback before executing the next. Typically the simplest way to do this is to nest writes inside the callback from the previous write. Listing 6.3 illustrates that process.

To write to a file asynchronously, first open it using open() and then after the callback from the open request has executed, use fs.write() to write data to the file. The following shows the syntax for fs.write():

fs.writeSync(fd, data, offset, length, position, callback)

The fd parameter is the file descriptor returned by openSync(). The data parameter specifies the String or Buffer object to be written to the file. The offset parameter specifies the index in the input data to begin reading data; if you want to begin at the current index in the String or Buffer, this value should be null. The length specifies the number of bytes to write; specifying null writes until the end of the buffer. The position argument specifies the position in the file to begin writing at; specifying null for this value uses the current file position.

The callback argument must be a function that can accept two parameters, error and bytes, where error is an error that occurred during the write and bytes specifies the number of bytes written.

Listing 6.3 illustrates implementing basic asynchronous writing to store a series of string data in a file. Notice that the callback specified in lines 18–20 in the open() callback calls the writeFruit() function and passes the file descriptor. Also notice that the write() callback specified in lines 6–13 also calls writeFruit() and passes the file descriptor. This ensures that the asynchronous write completes before executing another. Listing 6.3 Output shows the output of the code.

Listing 6.3 file_write_async.js: Performing asynchronous writes to a file

01 var fs = require('fs');
02 var fruitBowl = ['apple', 'orange', 'banana', 'grapes'];
03 function writeFruit(fd){
04   if (fruitBowl.length){
05     var fruit = fruitBowl.pop() + " ";
06     fs.write(fd, fruit, null, null, function(err, bytes){
07       if (err){
08         console.log("File Write Failed.");
09       } else {
10         console.log("Wrote: %s %dbytes", fruit, bytes);
11         writeFruit(fd);
12       }
13     });
14   } else {
15     fs.close(fd);
16   }
17 }
18 fs.open('fruit.txt', 'w', function(err, fd){
19   writeFruit(fd);
20 });

Listing 6.3 Output file_write_async.js: Writing asynchronously to a file

C:ooks
odech06writing>node file_write_async.js
Wrote: grapes  7bytes
Wrote: banana  7bytes
Wrote: orange  7bytes
Wrote: apple  6bytes

Streaming File Writing

One of the best methods to use when writing large amounts of data to a file is the streaming method. This method opens the file as a Writable stream. As discussed in Chapter 5, “Handling Data I/O in Node.js,” Writable streams can easily be implemented and linked to Readable streams using the pipe() method, which makes it easy to write data from a Readable stream source such as an HTTP request.

To stream data to a file asynchronously, you first need to create a Writable stream object using the following syntax:

fs.createWriteStream(path, [options])

The path parameter specifies the path to the file and can be relative or absolute. The optional options parameter is an object that can contain encoding, mode, and flag properties that define the string encoding as well as the mode and flags used when opening the file.

Once you have opened the Writable file stream, you can write to it using the standard stream write(buffer) methods. When you are finished writing, call the end() method to close the stream.

Listing 6.4 illustrates implementing a basic Writable file stream. Notice that when the code is finished writing, the end() method is executed on line 13, which triggers the close event. Listing 6.4 Output shows the output of the code.

Listing 6.4 file_write_stream.js: Implementing a Writable stream to allow streaming writes to a file

01 var fs = require('fs');
02 var grains = ['wheat', 'rice', 'oats'];
03 var options = { encoding: 'utf8', flag: 'w' };
04 var fileWriteStream = fs.createWriteStream("grains.txt",  options);
05 fileWriteStream.on("close", function(){
06   console.log("File Closed.");
07 });
08 while (grains.length){
09   var data = grains.pop() + " ";
10   fileWriteStream.write(data);
11   console.log("Wrote: %s", data);
12 }
13 fileWriteStream.end();

Listing 6.4 Output file_write_stream.js: Implementing streaming writes to a file

C:ooks
odech06writing>node file_write_stream.js
Wrote: oats
Wrote: rice
Wrote: wheat
File Closed.

Reading Files

The fs module also provides four different ways to read data from files. You can read data in one large chunk, read chunks of data using synchronous writes, read chunks of data using asynchronous writes, or stream reads through a Readable stream. Each of these methods is effective. Which one you should use depends on the particular needs of your application. The following sections describe how to use and implement these methods.

Simple File Read

The simplest method for reading data to a file is to use one of the readFile() methods. These methods read the full contents of a file into a data buffer. The following shows the syntax for the readFile() methods:

fs.readFile(path, [options], callback)
fs.readFileSync(path, [options])

The path parameter specifies the path to the file and can be relative or absolute. The optional options parameter is an object that can contain encoding, mode, and flag properties that define the string encoding as well as the mode and flags used when opening the file. The asynchronous method also requires a callback that is called when the file read has been completed.

Listing 6.5 illustrates implementing a simple asynchronous readFile() request to read a JSON string from a configuration file and then use it to create a config object. Listing 6.5 Output shows the output of the code.

Listing 6.5 file_read.js: Reading a JSON string file to an object

01 var fs = require('fs');
02 var options = {encoding:'utf8', flag:'r'};
03 fs.readFile('config.txt', options, function(err, data){
04   if (err){
05     console.log("Failed to open Config File.");
06   } else {
07     console.log("Config Loaded.");
08     var config = JSON.parse(data);
09     console.log("Max Files: " + config.maxFiles);
10     console.log("Max Connections: " + config.maxConnections);
11     console.log("Root Path: " + config.rootPath);
12   }
13 });

Listing 6.5 Output file_read.js: Reading a configuration file to an object

C:ooks
odech06
eading>node file_read.js
Config Loaded.
Max Files: 20
Max Connections: 15
Root Path: /webroot

Synchronous File Reading

The synchronous method of file reading reads the data from the file before returning execution to the running thread. This provides the advantage of allowing you to read multiple times in the same section of code, but this can be a disadvantage if the file reads hold up other threads as discussed earlier.

To read to a file synchronously, first open it using openSync() to get a file descriptor and then use readSync() to read data from the file. The following shows the syntax for readSync():

fs.readSync(fd, buffer, offset, length, position)

The fd parameter is the file descriptor returned by openSync(). The buffer parameter specifies the Buffer object that data will be read into from the file. The offset parameter specifies the index in the buffer to begin writing data; if you want to begin at the current index in the Buffer this value should be null. The length specifies the number of bytes to read; specifying null writes until the end of the buffer. The position argument specifies the position in the file to begin reading from; specifying null for this value uses the current file position.

Listing 6.6 illustrates implementing basic synchronous reading to read a chunk of string data from a file. Listing 6.6 Output shows the output of the code.

Listing 6.6 file_read_sync.js: Performing synchronous reads from a file

01 var fs = require('fs');
02 fd = fs.openSync('veggie.txt', 'r');
03 var veggies = "";
04 do {
05   var buf = new Buffer(5);
06   buf.fill();
07   var bytes = fs.readSync(fd, buf, null, 5);
08   console.log("read %dbytes", bytes);
09   veggies += buf.toString();
10 } while (bytes > 0);
11 fs.closeSync(fd);
12 console.log("Veg g (to get output shown) ies: " + veggies);

Listing 6.6 Output file_read_sync.js: Reading synchronously from a file

C:ooks
odech06
eading>node file_read_sync.js
read 5bytes
read 5bytes
read 5bytes
read 5bytes
read 2bytes
read 0bytes
Veggies: olives celery carrots

Asynchronous File Reading

The asynchronous method of file reading puts the read request on the event queue and then returns control back to the calling code. The actual read does not take place until the event loop picks up the read request and executes it. You need to be careful when performing multiple asynchronous read requests on the same file, since you cannot guarantee what order they will be executed unless you wait for the first read callback to execute before executing the next read. Typically the simplest way to do this is to nest reads inside the callback from the previous read. Listing 6.7 illustrates that process.

To read from a file asynchronously, first open it using open() and then after the callback from the open request has executed, use read() to read data from the file. The following shows the syntax for read():

fs.read(fd, buffer, offset, length, position, callback)

The fd parameter is the file descriptor returned by openSync(). The buffer parameter specifies the Buffer object that data will be read into from the file. The offset parameter specifies the index in the buffer to begin reading data; if you want to begin at the current index in the Buffer, this value should be null. The length specifies the number of bytes to read; specifying null reads until the end of the buffer. The position argument specifies the position in the file to begin reading from; specifying null for this value uses the current file position.

The callback argument must be a function that can accept three parameters: error, bytes, and buffer. The error parameter is an error that occurred during the read, bytes specifies the number of bytes read, and buffer is the buffer with data populated from the read request.

Listing 6.7 illustrates implementing basic asynchronous reading to read chunks of data from a file. Notice that the callback specified in lines 16–18 in the open() callback calls the readFruit() function and passes the file descriptor. Also notice that the read() callback specified in lines 5–13 also calls readFruit() and passes the file descriptor. This ensures that the asynchronous read completes before executing another. Listing 6.7 Output shows the output of the code.

Listing 6.7 file_read_async.js: Performing asynchronous reads from a file

01 var fs = require('fs');
02 function readFruit(fd, fruits){
03   var buf = new Buffer(5);
04   buf.fill();
05   fs.read(fd, buf, 0, 5, null, function(err, bytes, data){
06       if ( bytes > 0) {
07         console.log("read %dbytes", bytes);
08         fruits += data;
09         readFruit(fd, fruits);
10       } else {
11         fs.close(fd);
12         console.log ("Fruits: %s", fruits);
13       }
14   });
15 }
16 fs.open('fruit.txt', 'r', function(err, fd){
17   readFruit(fd, "");
18 });

Listing 6.7 Output file_read_async.js: Reading asynchronously from a file

C:ooks
odech06
eading>node file_read_async.js
read 5bytes
read 5bytes
read 5bytes
read 5bytes
read 5bytes
read 2bytes
Fruits: grapes banana orange apple

Streaming File Reading

One of the best methods to use when reading large amounts of data from a file is the streaming method. This method opens the file as a Readable stream. As discussed in Chapter 5, Readable streams can easily be implemented and linked to Writable streams using the pipe() method. This makes it easy to read data from a file and inject it into a Writable stream source such as an HTTP response.

To stream data from a file asynchronously, you first need to create a Readable stream object using the following syntax:

fs.createReadStream(path, [options])

The path parameter specifies the path to the file. The path can be relative or absolute. The optional options parameter is an object that can contain encoding, mode, and flag properties that define the string encoding as well as the mode and flags used when opening the file.

Once you have opened the Readable file stream, you can easily read from it using the readable event with read() requests or by implementing a data event handler as shown in Listing 6.8.

Listing 6.8 illustrates implementing a basic Readable file stream. Notice that lines 4–7 implement a data event handler that continuously reads data from the stream. Listing 6.8 Output shows the output of the code.

Listing 6.8 file_read_stream.js: Implementing a Readable stream to allow streaming reads from a file

01 var fs = require('fs');
02 var options = { encoding: 'utf8', flag: 'r' };
03 var fileReadStream = fs.createReadStream("grains.txt",  options);
04 fileReadStream.on('data', function(chunk) {
05   console.log('Grains: %s', chunk);
06   console.log('Read %d bytes of data.', chunk.length);
07 });
08 fileReadStream.on("close", function(){
09   console.log("File Closed.");
10 });

Listing 6.8 Output file_read_stream.js: Implementing streaming reads from a file

C:ooks
odech06
eading>node file_read_stream.js
Grains: oats rice wheat
Read 16 bytes of data.
File Closed.

Other File System Tasks

In addition to reading and writing files, the fs module also provides functionality for interacting with the file system—for example, listing files in a directory, looking at file information, and much more. The following sections cover the most common file system tasks that you may need to implement when creating Node.js applications.

Verifying Path Existence

Before doing any kind of read/write operation on a file or directory, you might want to verify whether the path exists. This can easily be done using one of the following methods:

fs.exists(path, callback)
fs.existsSync(path)

The fs.existsSync(path) returns true or false based on the path existence. Just as with any other asynchronous file system call, if you use fs.exists(), you need to implement a callback that is executed when the call completes. The callback is passed a Boolean value of true or false depending on whether the path exists. For example, the following code verifies the existence of a file named filesystem.js in the current path and displays the results:

fs.exists('filesystem.js', function (exists) {
  console.log(exists ? "Path Exists" : "Path Does Not Exist");
});

Getting File Info

Another common task is to get basic information about file system objects such as file size, the mode, modify time, whether the entry is a file or folder, and so on. This information can be obtained using one of the following calls:

fs.stats(path, callback)
fs.statsSync(path)

The fs.statsSync() method returns a Stats object, whereas the fs.stats() method is executed and the Stats object is passed to the callback function as the second parameter. The first parameter is error if an error occurs.

Table 6.2 lists some of the most commonly used attributes and methods attached to the Stats object.

Table 6.2 Attributes and methods of Stats objects for file system entries

Attribute/Method

Description

isFile()

Returns true if the entry is a file

isDirectory()

Returns true if the entry is a directory

isSocket()

Returns true if the entry is a socket

dev

Specifies the device ID on which the file is located

mode

Specifies the access mode of the file

size

Specifies the number of bytes in the file

blksize

Specifies the block size used to store the file in bytes

blocks

Specifies the number of blocks the file is taking on disk

atime

Specifies the time the file was last accessed

mtime

Specifies the time the file was last modified

ctime

Specifies the time the file was created

Listing 6.9 illustrates the use of the fs.stats() call by making the call and then outputting the results of the object as a JSON string as well as using the isFile(), isDirector(), and isSocket() calls, as shown in Listing 6.9 Output.

Listing 6.9 file_stats.js: Implementing an fs.stats() call to retrieve information about a file

01 var fs = require('fs');
02 fs.stat('file_stats.js', function (err, stats) {
03   if (!err){
04     console.log('stats: ' + JSON.stringify(stats, null, '  '));
05     console.log(stats.isFile() ? "Is a File" : "Is not a File");
06     console.log(stats.isDirectory() ? "Is a Folder" : "Is not a Folder");
07     console.log(stats.isSocket() ? "Is a Socket" : "Is not a Socket");
08     stats.isDirectory();
09     stats.isBlockDevice();
10     stats.isCharacterDevice();
11     //stats.isSymbolicLink(); //only lstat
12     stats.isFIFO();
13     stats.isSocket();
14   }
15 });

Listing 6.9 Output file_stats.js: Displaying information about a file

C:ooks
odech06>node file_stats.js
stats: {
  "dev": 818973644,
  "mode": 33206,
  "nlink": 1,
  "uid": 0,
  "gid": 0,
  "rdev": 0,
  "ino": 1970324837052284,
  "size": 535,
  "atime": "2016-09-14T18:03:26.572Z",
  "mtime": "2013-11-26T21:51:51.148Z",
  "ctime": "2014-12-18T17:30:43.340Z",
  "birthtime": "2016-09-14T18:03:26.572Z"
}
Is a File
Is not a Folder
Is not a Socket

Listing Files

Another common task when working with the file system is listing files and folders in a directory. For example, you might want to determine whether the files and folders need to be cleaned up, you might need to dynamically operate on the directory structure, and so on.

To access the files in the file system, use one of the following commands to read a list of entries:

fs.readdir(path, callback)
fs.readdirSync(path)

If readdirSync() is called, an array of strings representing the entry names in the specified path is returned. In the case of readdir(), the list is passed as the second parameter to the callback function and an error, if there is one, is passed as the first.

To illustrate the use of readdir(), Listing 6.10 implements a nested callback chain to walk the directory structure and output the entries. Notice that the callback function implements a wrapper to provide closure for the fullPath variable, and that the WalkDirs() function loops by being called by the asynchronous callback function, as shown in Listing 6.10 Output.

Listing 6.10 file_readdir.js: Implementing a callback chain to walk down and output the contents of a directory structure

01 var fs = require('fs');
02 var Path = require('path');
03 function WalkDirs(dirPath){
04   console.log(dirPath);
05   fs.readdir(dirPath, function(err, entries){
06     for (var idx in entries){
07       var fullPath = Path.join(dirPath, entries[idx]);
08       (function(fullPath){
09         fs.stat(fullPath, function (err, stats){
10           if (stats.isFile()){
11             console.log(fullPath);
12           } else if (stats.isDirectory()){
13             WalkDirs(fullPath);
14           }
15         });
16       })(fullPath);
17     }
18   });
19 }
20 WalkDirs("../ch06");

Listing 6.10 Output file_readdir.js: Iteratively walking the directory structure using chained asynchronous callbacks

C:ooks
odech06>node file_readdir.js
../ch06
..ch06file_readdir.js
..ch06filesystem.js
..ch06data
..ch06file_stats.js
..ch06file_folders.js
..ch06
enamed
..ch06
eading
..ch06writing
..ch06dataconfig.txt
..ch06datafolderA
..ch06datagrains.txt
..ch06datafruit.txt
..ch06
eadingfile_read.js
..ch06dataveggie.txt
..ch06datalog.txt
..ch06dataoutput.txt
..ch06writingfile_write.js
..ch06
eadingfile_read_async.js
..ch06
eadingfile_read_sync.js
..ch06
eadingfile_read_stream.js
..ch06writingfile_write_async.js
..ch06writingfile_write_stream.js
..ch06writingfile_write_sync.js
..ch06datafolderAfolderC
..ch06datafolderAfolderB
..ch06datafolderAfolderBfolderD
..ch06datafolderAfolderCfolderE

Deleting Files

Another common task when working with files is deleting them to clean up data or make more room on the file system. To delete a file from Node.js, use one of the following commands:

fs.unlink(path, callback)
fs.unlinkSync(path)

The unlinkSync(path) returns true or false based on whether the delete is successful. The asynchronous unlink() call passes back an error value to the callback function if an error is encountered when deleting the file.

The following code snippet illustrates the process of deleting a file named new.txt using the unlink() asynchronous fs call:

fs.unlink("new.txt", function(err){
  console.log(err ? "File Delete Failed" :  "File Deleted");
});

Truncating Files

Truncating a file means reducing the size of the file by setting the end to a smaller value than the current size. You might want to truncate a file that grows continuously but does not contain critical data, such as a temporary log. To truncate a file, use one the following fs calls and pass in the number of bytes you want the file to contain when the truncation completes:

fs.truncate(path, len, callback)
fs.truncateSync(path, len)

The truncateSync(path) returns true or false based on whether the file is successfully truncated. The asynchronous truncate() call passes an error value to the callback function if an error is encountered when truncating the file.

The following code snippet illustrates the process of truncating a file named log.txt to zero bytes.

fs.truncate("new.txt", function(err){
  console.log(err ? "File Truncate Failed" :  "File Truncated");
});

Making and Removing Directories

At times you may need to implement a directory structure for files being stored by your Node.js application. The fs module provides the functionality to add and remove directories as necessary.

To add a directory from Node.js, use one of the following fs calls. The path can be absolute or relative. The optional mode parameter allows you to specify the access mode for the new directory.

fs.mkdir(path, [mode], callback)
fs.mkdirSync(path, [mode])

The mkdirSync(path) returns true or false based on whether the directory is successfully created. The asynchronous mkdir() call passes an error value to the callback function if an error is encountered when creating the directory.

Keep in mind that when using the asynchronous method, you need to wait for the callback for the creation of the directory before creating a subdirectory. The following code snippet shows how to chain the creation of a subdirectory structure together:

fs.mkdir("./data/folderA", function(err){
  fs.mkdir("./data/folderA/folderB", function(err){
    fs.mkdir("./data/folderA/folderB/folderD", function(err){
    });
  });
  fs.mkdir("./data/folderA/folderC", function(err){
    fs.mkdir("./data/folderA/folderC/folderE", function(err){
    });
  });
});

To delete a directory from Node.js, use one of the following fs calls. The path can be absolute or relative.

fs.rmdir(path, callback)
fs.rmdirSync(path)

The rmdirSync(path) returns true or false based on whether the directory is successfully deleted. The asynchronous rmdir() call passes an error value to the callback function if an error is encountered when deleting the directory.

Just as with the mkdir() calls, keep in mind that when using the asynchronous method, you need to wait for the callback of the deletion of the directory before deleting the parent directory. The following code snippet shows how to chain the deletion of a subdirectory structure together:

fs.rmdir("./data/folderA/folderB/folderC", function(err){
  fs.rmdir("./data/folderA/folderB", function(err){
    fs.rmdir("./data/folderD", function(err){
    });
  });
  fs.rmdir("./data/folderA/folderC", function(err){
    fs.rmdir("./data/folderE", function(err){
    });
  });
});

Renaming Files and Directories

You might also need to rename files and folders in your Node.js application to make room for new data, archive old data, or apply changes made by a user. Renaming files and folders uses the fs calls shown here:

fs.rename(oldPath, newPath, callback)
fs.renameSync(oldPath, newPath)

The oldPath specifies the existing file or directory path, and the newPath specifies the new name. The renameSync(path) returns true or false based on whether the file or directory is successfully renamed. The asynchronous rename() call passes an error value to the callback function if an error is encountered when renaming the file or directory.

The following code snippet illustrates implementing fs calls to rename a file named old.txt to new.txt and a directory named testDir to renamedDir:

fs.rename("old.txt", "new.txt", function(err){
  console.log(err ? "Rename Failed" :  "File Renamed");
});
fs.rename("testDir", "renamedDir", function(err){
  console.log(err ? "Rename Failed" :  "Folder Renamed");
});

Watching for File Changes

Although not entirely stable, the fs module provides a useful tool to watch a file and execute a callback function when the file changes. This can be useful if you want to trigger events to occur when a file is modified, but do not want to continually poll from your application directly. This does incur some overhead in the underlying OS, so you should use watches sparingly.

To implement a watch on a file, use the following command passing the path to the file you want to watch. You can also pass in options, which is an object that contains persistent and interval properties. The persistent property is true if you want the process to continue to run as long as files are being watched. The interval property specifies the time in milliseconds that you want the file to be polled for changes:

fs.watchFile(path, [options], callback)

When a file change occurs, the callback function is executed and passes a current and previous Stats object.

The following code example monitors a file named log.txt at an interval of every 5 seconds and uses the Stats object to output the current and previous times the file was modified:

fs.watchFile("log.txt", {persistent:true, interval:5000}, function (curr, prev) {
  console.log("log.txt modified at: " + curr.mtime);
  console.log("Previous modification was: " + prev.mtime);
});

Summary

Node.js provides the fs module that allows you to interact with the file system. The fs module allows you to create, read, and modify files. You can also use the fs module to navigate the directory structure, look at information about files and folders, and change the directory structure by deleting and renaming files and folders.

Next

The next chapter focuses on using the http module to implement basic webservers. You see how to parse query strings and also how to implement a basic webserver in Node.js.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.118.87