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 notnull
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.