Vinics
Vinics

Reputation: 11

What is the best way to keep a file open to read/write?

I have a local JSON file which I intent to read/write from a NodeJS electron app. I am not sure, but I believe that instead of using readFile() and writeFile(), I should get a FileHandle to avoid multiple open and close actions.

So I've tried to grab a FileHandle from fs.promises.open(), but the problem seems to be that I am unable to get a FileHandle from an existing file without truncate it and clear it to 0.

const { resolve } = require('path');
const fsPromises = require('fs').promises;

function init() {
  // Save table name
  this.path = resolve(__dirname, '..', 'data', `test.json`);

  // Create/Open the json file
  fsPromises
    .open(this.path, 'wx+')
    .then(fileHandle => {
      // Grab file handle if the file don't exists 
      // because of the flag 'wx+'
      this.fh = fileHandle;
    })
    .catch(err => {
      if (err.code === 'EEXIST') {
        // File exists
      }

    });
}

Am I doing something wrong? Are there better ways to do it?


Links:
https://nodejs.org/api/fs.html#fs_fspromises_open_path_flags_mode
https://nodejs.org/api/fs.html#fs_file_system_flags

Upvotes: 1

Views: 2511

Answers (1)

jfriend00
jfriend00

Reputation: 707318

Because JSON is a text format that has to be read or written all at once and can't be easily modified or added onto in place, you're going to have to read the whole file or write the whole file at once.

So, your simplest option will be to just use fs.promises.readFile() and fs.promises.writeFile() and let the library open the file, read/write it and close the file. Opening and closing a file in a modern OS takes advantage of disk caching so if you're reopening a file you just previously opened not long ago, it's not going to be a slow operation. Further, since nodejs performs these operations in secondary threads in libuv, it doesn't block the main thread of nodejs either so its generally not a performance issue for your server.

If you really wanted to open the file once and hold it open, you would open it for reading and writing using the r+ flag as in:

const fileHandle = await fsPromises.open(this.path, 'r+');

Reading the whole file would be simple as the new fileHandle object has a .readFile() method.

 const text = await fileHandle.readFile({encoding 'utf8'});

For writing the whole file from an open filehandle, you would have to truncate the file, then write your bytes, then flush the write buffer to ensure the last bit of the data got to the disk and isn't sitting in a buffer.

await fileHandle.truncate(0);                         // clear previous contents
let {bytesWritten} = await fileHandle.write(mybuffer, 0, someLength, 0);   // write new data
assert(bytesWritten === someLength);
await fileHandle.sync();                              // flush buffering to disk

Upvotes: 3

Related Questions