Will fs.readFile() cache the file's contents in the server's memory after the first read?

Viewed 2062

I would like to know if the following code would cache the file's contents in the server's memory after reading it once. The reason I ask is because I don't want to have to re read the file every time the user requested the page. I would prefer to have it cached after the first read.

fs.exists(fileName, function (exists) {
        if (!exists) {
            console.log("== 404 error");
            resp.writeHead(404, {'Content-Type': 'text/html'});
            resp.end(pageError);
            return;
        }

        fs.readFile(fileName, 'utf8', function (err, data) {
            if (err) {
                resp.writeHead(404, {"Content-Type": "text/html"});
                resp.end(pageError);
                return;
            }

            var contentType = getContentType(req.url);
            var mimeType = mimeTypes[contentType];

            resp.writeHead(200, {"Content-Type": mimeType});
            resp.end(data);
        });
    });

NOTE ** I only want to know how to do this using internal Node JS modules (no express)

3 Answers

You shouldn't use fs.exists() as its deprecated; instead, use fs.stat() if you only want to check existence. If you are going to open and read a file after checking for existence, then just use fs.readFile() and handle the passed error accordingly for a not existing file. This is noted within the fs docs for fs.access() but still applies to fs.stat() as well. Below is the excerpt from the Node.js docs.

Using fs.access() to check for the accessibility of a file before calling fs.open(), fs.readFile() or fs.writeFile() is not recommended. Doing so introduces a race condition, since other processes may change the file's state between the two calls. Instead, user code should open/read/write the file directly and handle the error raised if the file is not accessible.

fs.readFile() does not do any caching for you, this is something you'll need to create/manage yourself. The below example shows how to create a file cache using a JS Object as a dictionary to keep the file contents indexed by filename. Its important to note that you shouldn't be putting gigs of data in the fileCache object, instead this will be good for lots of smaller files.

fileCache just needs to be in scope of getFileFromCache() and in a place that won't be garbage collected during runtime.

const fileCache = {}
const getFileFromCache = (filename, cb) => {
    if (fileCache[filename]) {
        return cb(null, fileCache[filename])
    }

    fs.readFile(filename, 'utf8', (err, data) => {
      if (err) {
        return cb(err)
      }

      fileCache[filename] = data
      return cb(null, data)     
    })
}

You could store the file data in a variable, or in a global variable (by using global.<varname> = <filedata>) if you want to access it across multiple modules.

Of course, as George Cambpell said, anoy modification to the file won't be noticed by your program, since it won't re-read the file.

So I would do something like this:

function sendResponse(data) {
    let contentType = getContentType(req.url);
    let mimeType = mimeTypes[contentType];
    resp.writeHead(200, {"Content-Type": mimeType});
    resp.end(data);
}

if(global.fileData) {
    return sendResponse(global.fileData);
}

fs.readFile(fileName, 'utf8', function (err, data) {
    if (err) {
        resp.writeHead(404, {"Content-Type": "text/html"});
        resp.end(pageError);
        return;
    }

    global.fileData = data;
    sendResponse(global.fileData);
});

The first time global.fileData will be empty, so you'll proceed with fs.readfile, store the file content in global.fileData, and send the response.
The second time global.fileData will contain stuff, so you just send the response with that stuff, and you won't read the file again.
For further reference take a look at the official NodeJS documentation: https://nodejs.org/api/globals.html#globals_global

Another thing you should do is replace fs.exists with fs.access or fs.stat (I usually use fs.access), because the exists method is deprecated.
https://nodejs.org/api/fs.html#fs_fs_stat_path_callback
https://nodejs.org/api/fs.html#fs_fs_access_path_mode_callback

Happy coding!

Will fs.readFile() cache the file's contents in the server's memory after the first read?

No. fs.readFile() itself does no caching.

But, the underlying operating system will do file caching and as long as there isn't so much other file activity going on that the cached read gets flushed, then the OS will probably fetch the file from a local memory cache the 2nd, 3rd times you read it.

If you want to assure the caching yourself, then you should just store the contents yourself once you first read it and from then on, you can just use the previously read contents.

Related