Another feature of the fs module is the ability to read the contents of files. We can read the contents of both text and binary files. If we are reading the contents of a text file, we have to make sure that we send the read file function a text encoding, like UTF-8, otherwise it will automatically read our files as binary, giving us the Node.js buffer class.
Let’s go ahead and look at an example of this by trying to read some files. Let’s navigate to our exercise files, you will notice that we have a lib folder that contains a couple of files, a JSON file and a Markdown file.
So we’re going to read this Markdown document. Whenever we’re reading a text file, the second argument is going to be the text encoding, so we’re going to read this file as UTF-8. So when we call readFile and we send the encoding, the contents of the file will be text. So let’s just go ahead and log the contents. Great, and I’m going to go ahead and save this and navigate out to my Terminal, and let’s run it, node read, and you can see that when we read the file, we are actually reading the contents of that Markdown file.
It has some Ben Franklin and George Washington quotes. So what happens if we do not send a text encoding? Let’s go back to our code, and instead of sending it a UTF-8 encoding, why don’t we go ahead and delete the UTF-8, and let’s just read the file. So we’ll go ahead and read that sayings Markdown document, and let’s go back out to our Terminal, and I’m going to clear these last results. And we’ll go ahead and read our file again, and we can see that we’re reading the same file. Whenever we are reading a file without a text encoding, we are reading that file data as binary.
And binary files in Node.js are handled with the Node.js buffer class. So it is very important if we’re reading text files to send that text encoding. Reading files can be handled synchronously or asynchronously, and most of the time we probably want to handle them asynchronously. So I can get rid of the Sync, and I can also get rid of setting the contents from what is returned from the readFile function. The difference when we read files asynchronously, is that UTF-8, or text format encoding, will happen as the second argument in our callback function, that will fire once we have read the file, becomes the third argument.
So if there were any errors in the process of reading the file, those will be passed to this function, and the contents will be the second argument passed to the callback function. So instead of logging the contents down here on line 7, I’m going to go ahead and log them up here within the callback, once we’ve actually received the file contents asynchronously. This time if we have an error, I’m just going to log that error to the console. If I throw the error, it will cause the error to get thrown, and it will actually kill the process. Logging the error doesn’t kill the process, but it will just show the users that some sort of an error has occurred.
So what I’m going to do is save this, and let’s go back to our Terminal, and we are going to read the file. And as you can see, we read the contents of the same file, asynchronously. Let’s go ahead and combine the readFile command with the readdir command, and read the contents of all of the files found in a directory. So let’s go back to our files, and now I want to include the path module that is going to help us deal with file paths, so var path = require, the path module, and we are going to actually just delete this readFile call, and what I’m going to go ahead and do is start off with a readdir, so we’ll do a fs.readdir call, and we are going to read the contents of the lib directory.
So the very first thing that I want to do is I want to actually create a full path for the file. So I’m going to go ahead and use var file to create a full path using path.join the current directory, with the lib directory, and then the fileName. So that should create a full path to our current file inside of the lib directory. Now the other thing we need to do is get some statistics about this file. So I’m going to create a variable for stats and we can use the fs.stat function to get file statistics.
And I’m going to do this synchronously. This will occur very fast, but I’m going to get the file statistics for this current file synchronously. So I can go ahead and add the file to our fs.statSync. Now the stats will tell me whether this is a file or directory, so in my next line I’m going to go ahead and use that. So if stats.isFile, right, and fileName !== to that .DS_Store. So if we have a file and the file is not equal to that hidden DS_Store file, we are actually going to read the contents.
So in this case, I’m going to go ahead and say fs.readFile, and I’m going to read the contents of our file, and I’m going to assume it is text, so we will read those contents as UTF-8. And when I have those file contents, this callback function will be invoked. If there are any errors reading the file, that will be passed as an error, otherwise, the file contents will be passed as the second argument to this function. So let’s go ahead and then just log those contents.
Great, so here we’re combining several file system methods to read all of the files in the directory. Let’s go ahead and save this and go out to our Terminal, and let’s clear our previous results, and let’s go ahead and read our file. And as you can see, we actually are seeing a JSON file, which is found in the directory, and we’re also seeing the contents of the Markdown file, which is found in the directory. We are not reading the contents of any subdirectories, and we are also not reading the DS_Store contents.
So the fs module is going to allow us to do a lot of awesome things with the file system, and we can use the fs module’s functions in conjunction with one another to say read all of the text files in a directory.