Debotos Das
Debotos Das

Reputation: 569

In Node.js how to check a file is currently open?

If a file is currently opened via native application(.pdf -> pdf viewer) how do I track down its open status?

I am creating a file syncing application using electron.js. So if the user wants to remove a file I wanna check is it open or not. If open I want to show a warning.

File is still open. Please close it first.

Upvotes: 3

Views: 1728

Answers (3)

Debotos Das
Debotos Das

Reputation: 569

For now, the below snippet is working for me(Tested in macOS) -

In the main process -

import util from 'util'
const exec = util.promisify(require('child_process').exec)

const checkFileIsOpen = async (fileName: string): Promise<boolean> => {
    try {
        const filePath = myAppDocsPath + '/' + fileName.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')

        const { stdout } = await exec('lsof ' + filePath)
        console.log('stdout:', stdout)

        if (stdout.length === 0) {
            // Not in use | File is not open
            return false
        } else {
            // In use | File is open
            return true
        }
    } catch (error) {
        console.log('Check file is open error:\n', error)
        return false
    }
}

Upvotes: 2

Debotos Das
Debotos Das

Reputation: 569

I found a better solution with the help of a package (https://github.com/ronomon/opened) -

const checkFileIsOpen = async (fileName: string): Promise<boolean> => {
    try {
        // myAppDocsPath is a variable of my application specific path
        const filePath = myAppDocsPath + '/' + fileName
        const paths = [filePath]
        return new Promise((resolve, reject) => {
            Opened.files(paths, function (error: any, hashTable: Record<string, boolean>) {
                console.log(hashTable)
                if (error) throw reject(false)
                resolve(hashTable[paths[0]])
            })
        })
    } catch (error) {
        console.log('Check file is open error:\n', error)
        return false
    }
}

For a list of file -

type FileListOpenStatus = Record<string, boolean> | false

const checkFileListOpenStatus = async (fileNameList: string[]): Promise<FileListOpenStatus> => {
    try {
        // myAppDocsPath is a variable of my application specific path
        const paths = fileNameList.map((fileName) => `${myAppDocsPath}/${fileName}`)
        return new Promise((resolve, reject) => {
            Opened.files(paths, function (error: any, hashTable: Record<string, boolean>) {
                console.log(hashTable)
                if (error) throw reject(false)
                const results: Record<string, boolean> = {}
                for (const [filePath, value] of Object.entries(hashTable)) {
                    const fileName = path.basename(filePath)
                    results[fileName] = value
                }
                resolve(results)
            })
        })
    } catch (error) {
        console.log('Check file list open status error:\n', error)
        return false
    }
}

Upvotes: 0

leitning
leitning

Reputation: 1506

Using the spawn function you receive a reference to the child process that you can listen to the exit event on. Here's a simple example

const {spawn} = require('child_process');
const PDF_BIN = '/path/to/pdf-viewer';

const fileTracker = {};

const openFile = (fileName) => {
  if(fileTracker[fileName])
    throw new Error(`${fileName} already open`);
  let args = ['-foo','--b','ar',fileName];
  let cp = spawn(PDF_BIN, args);
  fileTracker[fileName] = cp;
  cp.once('error',(err) => {
    console.error(err);
    console.log(`Error opening ${fileName}`);
    delete(fileTracker[fileName]);
  });
  cp.once('exit',(code,signal) => {
    if(signal)
      code = signal;
    if(code != 0)
      console.error(`Recieved exit code ${code} on file ${fileName}`);
    delete(fileTracker[fileName]);
  })  
}

Upvotes: 0

Related Questions