Writing Files and Directories with Node.js's fs Module
Tags: node, beginners, javascript, tutorial
Published: 2020-01-05 22:35:00

**Subscribe to my email list now at http://jauyeung.net/subscribe/**

**Follow me on Twitter at https://twitter.com/AuMayeung**

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:

```javascript
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:

```bash
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:

```javascript
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:

```bash
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 the`writeSync` 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:

```javascript
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:

```javascript
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:

```javascript
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:

```javascript
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.