Categories
JavaScript Nodejs

Node.js’s ‘fs’ Module: Writing Files and Directories

Spread the love

Manipulating files and directories are basic operations for any program. Since Node.js is a server-side platform and can interact with the computer that it’s running on directly, the ability to manipulate files is a basic feature.

Fortunately, Node.js has a fs module built into its library. It has many functions that can help with manipulating files and folders. It supports basic file and directory operations, like manipulating and opening files in directories.

It can do the same for files. It can do this both synchronously and asynchronously. It has an asynchronous API with functions that support promises. Also, it can show statistics for a file.

Almost all the file operations that we can think of can be done with the built-in fs module. In this piece, we’ll use the functions in the fs module to write files with the write family of functions to write files.


fs.Write

There are two versions of the write functions, one for writing text to disk and another for writing binary data to disk.

The text version of write function lets us write text onto the disk asynchronously. It takes a few arguments.

The first argument is the file descriptor — a number that identifies the file.

The second argument is a string that’s written to the file. If the value passed in is not a string, it is converted to one.

The third argument is the position in which the file writing starts. If the value passed in isn’t a number, then it starts in the current position.

The fourth argument is a string that has the character encoding of the file to be written, which defaults to be utf8. The last argument is a callback function with three parameters.

The first is the err object which has the error object and it’s not null if there’s an error.

The second parameter is the written parameter, an integer which specifies how many bytes are written to the file system. It’s not necessarily the same as the number of string characters written.

The third parameter is the string parameter that has the string that was written.

On Linux, positional writes don’t work in the append model. On Windows, if the file descriptor is 1, which stands for the standard output, then strings that have non-ASCII characters won’t be rendered properly by default.

To use the write function, we can use the open function to get the file descriptor of the file you want to write to first, then we can write to the file by passing in the file descriptor to the write function. For example, if we want to write to the file with the path ./files/file.txt , we can write something like this:

const fs = require("fs");
fs.open("./files/file.txt", "r+", (err, fd) => {  
  if (err) throw err;  
  fs.write(fd, "abc", 0, "utf8", (err, written, string) => {  
    console.log(err, written, string);  
    fs.close(fd, err => {  
      if (err) throw err;  
    });  
  });  
});

When we run the code above, we should get output that looks something like this:

null 3 abc

In the code above, we first open the file with the open function. We pass in the r+ flag so that we can write to the file. Then we get the file descriptor fd in the callback function that we passed into the open function. With the fd file descriptor, we can pass it into the write function.

In the second argument of the write function we specified that we want to write the string abc to the file. In the third argument, we specified that we want to write it at position 0, the fourth argument specifies that the character encoding of the string should be UTF-8.

The callback in the last argument would get us the result of the write. From there, we know from the output that three bytes and the string ‘abc’ were written to the file.

Other than the r+ flag, there are many other possible system flags, including:

  • 'a' — Opens a file for appending, which means adding data to the existing file. The file is created if it does not exist.
  • 'ax' — Like 'a' but an exception is thrown if the path exists.
  • 'a+' — Open file for reading and appending. The file is created if it doesn’t exist.
  • 'ax+' — Like 'a+' but an exception is thrown if the path exists.
  • 'as' — Opens a file for appending in synchronous mode. The file is created if it does not exist.
  • 'as+' — Opens a file for reading and appending in synchronous mode. The file is created if it does not exist.
  • 'r' — Opens a file for reading. An exception is thrown if the file doesn’t exist.
  • 'r+' — Opens a file for reading and writing. An exception is thrown if the file doesn’t exist.
  • 'rs+' — Opens a file for reading and writing in synchronous mode.
  • 'w' — Opens a file for writing. The file is created (if it does not exist) or overwritten (if it exists).
  • 'wx' — Like 'w' but fails if the path exists.
  • 'w+' — Opens a file for reading and writing. The file is created (if it does not exist) or overwritten (if it exists).
  • 'wx+' — Like 'w+' but an exception is thrown if the path exists.

The binary version of the write function lets us write text onto the disk asynchronously. It takes a few arguments.

The first argument is the file descriptor which is a number that identifies the file.

The second argument is the buffer object which can be of type Butter, TypedArray or DataView.

The third argument is the offset , which determines the part of the buffer to be written.

The fourth argument is the length argument that specifies the number of bytes being written, the last argument is the position which is an integer which describes the position in which the write function will start writing.

The final argument is a callback function — a function that takes the err parameter, which has the error object. If an error occurs, the second is the bytesWritten parameter which gets us the number of bytes written to disk, the third is the buffer object, which has the binary data which was written to disk.

For example, we can use it as in the following code:

const fs = require("fs");
fs.open("./files/binaryFile", "w", (err, fd) => {  
  if (err) throw err;  
  fs.write(fd, new Int8Array(8), 0, 8, 0, (err, bytesWritten, buffer) => {  
    console.log(err, bytesWritten, buffer);  
    fs.close(fd, err => {  
      if (err) throw err;  
    });  
  });  
});

We get the following output if we run it:

null 8 Int8Array [  
  0, 0, 0, 0,  
  0, 0, 0, 0  
]

fs.writeSync

The synchronous version of the write function is the writeSync function. There’s one version for writing binary data and one for writing text data.

The text version of thewriteSync function lets us write text onto the disk asynchronously. It takes a few arguments.

The first argument is the file descriptor which is a number that identifies the file.

The second argument is a string that will be written to the file. If the value passed in is not a string, it will be converted to one.

The third argument is the position in which the file writing starts. If the value passed in isn’t a number, then it starts in the current position.

The fourth argument is a string that has the character encoding of the file be written, which defaults to be utf8. The number of bytes written is returned.

We can use the text version of writeSync function as in the following code:

const fs = require("fs");
const fd = fs.openSync("./files/file.txt", "r+");  
const numBytesWritten = fs.writeSync(fd, "abc", 0, "utf8");  
console.log(numBytesWritten);

We should get 3 in the console.log statement since 3 bytes were written.

The binary data version of the writeSync function takes five arguments. It takes a file descriptor as the first argument. The second argument is the buffer object, which can be a Buffer, TypedArray or DataView object.

The third argument is the offset, which is the integer which specifies the part of the buffer to which the writeSync function will start writing.

The fourth argument is the length argument, which specifies the number of bytes being written.

The last argument is the position, which is an integer that describes the position in which the writeSync function will start writing. The number of bytes written is returned.

We can use the binary version of the writeSync function as in the following code:

const fs = require("fs");
const fd = fs.openSync("./files/binaryFile", "w");  
const numBytesWritten = fs.writeSync(fd, new Int8Array(8), 0, 8, 0);  
console.log(numBytesWritten);

We should get 8 in the console.log statement since 8 bytes were written.


fs.writeFile

We can write files in a less complicated way with the fs.writeFile function. It takes four arguments.

The first argument is the reference to the file, which can be a string path, a Buffer object, a URL object or a file descriptor, which is an integer that identifies the file.

The second argument is the data to be written to the file, which can be a string, a Buffer object, a TypedArray, or a DataView object. The third arguments is for passing in an object with a few options.

There are three things that can be set: the encoding, the mode, and the flag:

  • The encoding specifies the character encoding of the text being written which defaults to utf8 .
  • The mode is an integer which defaults to 0o666. The mode sets the file permission and the sticky bits but only if the file was already created. 0o666 means both readable and writable.
  • The flag option specifies the read or writes options of the file being written. The ones listed above are all valid for this argument. The last argument is the callback function, which is a function that takes an err object which is not null if an error exists.

For example, if we want to write to the file with the path ./files/file.txt with the writeFile function, we can write something like this:

const fs = require("fs");
fs.writeFile(  
  "./files/file.txt",  
  "abc",  
  { encoding: "utf8", mode: 666, flag: "w" },  
  err => console.log(err)  
);

In the first argument, we pass in the string path of the file. Then, in the second argument, we passed in the content to be written. The third argument has the encoding options, file permission of the file being written with the mode option, and the flag to set the mode that the file is open with. If the file is written successfully. The console.log statement above should output null and that you can see the content of the file you wrote onto the disk.

When a file descriptor is passed into the first argument of the writeFile function, the behavior is slightly different from passing in other objects to reference the file. The file is not replaced if a file descriptor is passed into the first argument.

This means that if the writeFile function is called multiple times with the file descriptor, then the item that’s written to the file will be appended to the file instead of overwriting the existing content. So if the first writeFile call passes in the file descriptor 1 and content a and the second writeFile call passes in the file descriptor 1 and content b, then we get ab in the file with file descriptor 1.


fs.writeFileSync

There’s a synchronous version of writeFile called writeFileSync, which takes the same arguments as the writeFile function except without the callback. It returns undefined . We can use it as in the following function:

const fs = require("fs");
fs.writeFileSync("./files/file.txt", "abc", {  
  encoding: "utf8",  
  mode: 666,  
  flag: "w"  
});

After running the code above, you should see the content written to disk in the file with the given path.

Manipulating files and directories are basic operations for any program. Since Node.js is a server-side platform and can interact with the computer that it’s running on directly, being able to manipulate files is a basic feature. Fortunately, Node.js has a fs module built into its library.

It has many functions that can help with manipulating files and folders. In this piece, we used the functions in the fs module to write files with the write family of functions to write files. We used the open and write functions to first open the file to get the file descriptor, then to write the content to the file. It has separate versions to work with text and binary files.

There’s also a synchronous version of the function called writeSync which takes the same arguments without the callbacks. It also has a text and binary version. To make writing to files more convenient, we can use the writeFile and the writeFileSync function, which can write both text and binary files given reference to a file like a path string or a URL object. writeFile is asynchronous and writeFileSync is synchronous.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *