Anaxion
Anaxion

Reputation: 43

Wait until file is created to read it in node.js

I am trying to use file creation and deletion as a method of data transfer (not the best way, I know.) between python and nodejs. The python side of the program works fine, as I am quite familiar with python 3, but I can't get the node.js script to work.

I've tried various methods of detecting when a file is created, mainly with the use of try {} catch {}, but none of them have worked.

function fufillRequest(data) {
  fs.writeFile('Response.txt', data)
}

while(true) {
  try {
    fs.readFile('Request.txt', function(err,data) {
      console.log(data);
    });
  } catch {

  }
}

The program is supposed to see that the file has been created, read it's contents, delete it and then create and write to a response file.

Upvotes: 4

Views: 6040

Answers (3)

ggorlen
ggorlen

Reputation: 56855

Existing answers are a bit old fashioned with async callbacks. Here's a more modern promise-based version tested in Node 18:

const fs = require("node:fs/promises");
const timers = require("node:timers/promises");

const waitForFile = async (
  filePath,
  {timeout = 30_000, delay = 200} = {}
) => {
  const tid = setTimeout(() => {
    const msg = `Timeout of ${timeout} ms exceeded waiting for ${filePath}`;
    throw Error(msg);
  }, timeout);

  for (;;) {
    try {
      const file = await fs.readFile(filePath, {encoding: "utf-8"});
      clearTimeout(tid);
      return file;
    }
    catch (err) {}

    await timers.setTimeout(delay);
  }
};

// test it:
(async () => {
  const file = "test.txt";
  setTimeout(() => fs.writeFile(file, "hello world!"), 3000);
  const content = await waitForFile(file);
  console.log(content);
  await fs.unlink(file);
})();

If you want, you can remove fs.readFile from the waitForFile function if you just want to know it exists or leave responsibility to the caller, rather than automatically read it. In this version, the for loop body would look like:

try {
  await fs.stat(filePath);
  clearTimeout(tid);
  return;
}
catch (err) {}

Upvotes: 1

jfriend00
jfriend00

Reputation: 707158

You can either user a recurring timer or fs.watch() to monitor when the file appears.

Here's what it would look like with a recurring timer:

const checkTime = 1000;
const fs = require('fs');

function check() {
   setTimeout(() => {
       fs.readFile('Request.txt', 'utf8', function(err, data) {
          if (err) {
              // got error reading the file, call check() again
              check();
          } else {
              // we have the file contents here, so do something with it
              // can delete the source file too
          }
       });
   }, checkTime)
}

check();

Note: Whatever process is creating this file should probably use an exclusive access mode when writing so that you don't create a race condition where it starts reading before the other process is done writing.

Upvotes: 2

xdeepakv
xdeepakv

Reputation: 8125

@jfriend00 solution is correct. However, In the above solution. It never cleans the timeout. It may cause an issue. If u need blocking code and better timer handling u can use setInterval.

Sample:

const checkTime = 1000;
var fs = require("fs");
const messageFile = "test.js";
const timerId = setInterval(() => {
  const isExists = fs.existsSync(messageFile, 'utf8')
  if(isExists) {
    // do something here
    clearInterval(timerId)
  }
}, checkTime)

You can also run your python program. No need to write another script.

const spawn = require("child_process").spawn;
const proc = spawn('python',["./watch.py"]);

proc.stdout.on('data', (data) => console.log(data.toString()))
proc.stderr.on('data', (data) => console.log(data.toString()))

Upvotes: 4

Related Questions