Reading files with Node

By

Learn how to read files in Node.js with the fs module, using the asynchronous readFile() and synchronous readFileSync(), and why streams suit big files.

~~~

The simplest way to read a file in Node is to use the fs.readFile() method, passing it the file path and a callback function that will be called with the file data (and the error):

const fs = require('fs')

fs.readFile('/Users/flavio/test.txt', (err, data) => {
  if (err) {
    console.error(err)
    return
  }
  console.log(data)
})

Run this and the output might surprise you. Instead of the file text you get the raw bytes:

<Buffer 48 65 6c 6c 6f>

That’s because there is no default string encoding: if you don’t specify one, Node hands you a Buffer object. Pass the encoding as the second parameter, before the callback, to get a string:

fs.readFile('/Users/flavio/test.txt', 'utf8', (err, data) => {
  if (err) {
    console.error(err)
    return
  }
  console.log(data) //Hello
})

Alternatively, you can use the synchronous version fs.readFileSync():

const fs = require('fs')

try {
  const data = fs.readFileSync('/Users/flavio/test.txt', 'utf8')
  console.log(data)
} catch (err) {
  console.error(err)
}

The trade-off is right in the name. readFileSync() blocks the entire process until the file is read. In a small command-line script that’s perfectly fine, and the code is easier to follow. In a server, it freezes every other request while the disk works — use the asynchronous versions there.

There is also a promise-based API in fs/promises, which pairs nicely with await:

const fs = require('fs/promises')

async function main() {
  const data = await fs.readFile('/Users/flavio/test.txt', 'utf8')
  console.log(data)
}

main()

When the file is missing

The most common failure is a wrong path. You get:

Error: ENOENT: no such file or directory, open '/Users/flavio/test.txt'

ENOENT means the file does not exist at that path. Check err.code === 'ENOENT' when a missing file is an expected case you want to handle gracefully, instead of treating every error the same way.

Big files

Both fs.readFile() and fs.readFileSync() read the full content of the file in memory before returning the data.

This means that big files are going to have a major impact on your memory consumption and speed of execution of the program. A 2 GB log file means 2 GB of RAM, just to look at it.

In this case, a better option is to read the file content using streams: fs.createReadStream() gives you the file in small chunks, so memory use stays flat no matter how large the file is.

Tagged: Node.js · All topics
~~~

Related posts about node: