File System

Learn to use the file system module in Node.js.

The fs module

fs is a built-in Node.js module that allows us to interact with the file system. It has quite a few methods, some of which have both synchronous and asynchronous variants. Before we move ahead with code examples, it should be noted that the asynchronous forms of the methods always take a completion callback as the last argument. For this callback, the first argument is reserved for an exception. This will be easier to understand once we see the code.

Reading files

The fs module provides an easy way to read files using the fs.readFile method. It is passed a file path, optional arguments, such as encoding, and a callback function that will be called with data read from the file. Let’s look at both the asynchronous and synchronous versions of this method.

index.js
test.txt
const fs = require('fs')
fs.readFile('test.txt', 'utf-8', (err, data) => {
if (err) {
console.error(err)
return
}
console.log(data)
})
console.log("Hello!")
Hit the RUN button to view the output

We need to import the fs module in order to use the readfile method. Both codes read from the test.txt file. This is the first argument passed to both methods. The second argument we passed is the encoding, utf-8 in our case. This ensures that the file is read in the same way it was stored.

  • The readFile method is asynchronous and, hence, takes a completion callback as its last argument. This callback takes an err as an exception. If no exception occurs, this will be either null or undefined. The second argument of this callback is data. This represents the data that is read from the file. Since this is asynchronous, the process does not block and we see our Hello! before the file data.
  • The readFileSyncmethod,as the name suggests, is synchronous. We have placed this in a try catch block, as it is good coding practice and shall keep the process from terminating in case we are unable to read the file. Unlike the asynchronous method, this blocks the program until the file is read. This is why we see the file data first, followed by the Hello! in our output.

Both of these methods read the entire file into memory before they return. For very large files, this may impact our system memory consumption.

Try changing the arguments passed to both code examples to see how the output might change.

Writing to files

We can write to files in a similar manner as reading them. The aptly named writeFile and the writeFileSync methods can be used for this job. Let’s look at the code below.

const fs = require('fs')
let content = "This is what will be written to the file"
fs.writeFile('test.txt', content, (err) => {
if (err) {
console.error(err)
return
}
console.log("File written!")
})
fs.readFile('test.txt', 'utf-8', (err, data) => {
if (err) {
console.error(err)
return
}
console.log(data)
})
Hit the RUN button to view the output

We are writing to the file named test.txt. To check that the file was indeed written to, we read it again and output its content. What if we want to overwrite a file, or perhaps, create a file if it does not exist before writing to it? For these, we have flags. flags are optional parameters passed to different methods of the fs module. These allow us to dictate how we would like to interact with the file. Check out all of the flags available here.

File stats

Sometimes, we need to do more than just read or write to a file. The fs module has a stat method that provides us with a lot of useful information about files. Let’s see how it works.

Press + to interact
index.js
test.txt
const fs = require('fs')
fs.stat('test.txt', (err, stats) => {
if (err) {
console.error(err)
return
}
console.log(stats)
})

File descriptors

Node.js also provides a way to use file descriptors. If you are not familiar with file descriptors, you can simply think of them as a number that the OS uses to keep track of an open file. However, the readFile and writeFile methods discussed earlier provide an easier method to read and write to files. Let’s see how we can use file descriptors in Node.js.

Press + to interact
index.js
test.txt
const fs = require("fs");
var fileName = "test.txt";
fs.stat(fileName, (err, stats) => {
if (err) {
console.error(err);
return;
}
fs.open(fileName, "r", (err, fd) => {
var buffer = Buffer.alloc(stats.size);
fs.read(fd, buffer, 0, buffer.length, null, (err, bytesRead, buffer) => {
var data = buffer.toString("utf8", 0, buffer.length);
console.log(data);
fs.close(fd, (err) => {
if (err) {
console.error(err);
return;
}
});
});
});
});
  • We use the stat method to get the size of the file being read. This size is used to create the buffer.

  • The open method returns the file descriptor for the file at the path we passed as an argument. The argument r is the file system flag. These flags allow us to access files in different ways. You can read up on all the flags here.

  • The read function on line 13 takes quite a few arguments. The first one being the file descriptor. The next 4 are options:

    • buffer is the Buffer that will be written to once the file descriptor is read. This is the buffer we created on line 11 in our case.
    • offset is the offset after which the buffer will be written to. Incase our buffer was not empty, we can specify this offset to begin writing after the previous content. This is 0 in our case.
    • length corresponds to the number of bytes to be read. Since we want to read the entire file, we have set this to buffer.length.
    • position refers to where to begin reading the file. Setting it to null begins reading from the current file position.

    Finally, a callback function is passed. This function returns an error, bytesRead and buffer. The bytesRead is the number of bytes read into the buffer.

  • We use the close method to close the file descriptor.

The fs module is a very versatile and easy-to-use module that makes file handling possible with Node.js.

svg viewer

Get hands-on with 1300+ tech skills courses.