NightMICU
NightMICU

Reputation: 9240

Get the most recent file in a directory, Node.js

I am trying to find the most recently created file in a directory using Node.js and cannot seem to find a solution. The following code seemed to be doing the trick on one machine but on another it was just pulling a random file from the directory - as I figured it might. Basically, I need to find the newest file and ONLY that file.

var fs = require('fs'); //File System
var audioFilePath = 'C:/scanner/audio/'; //Location of recorded audio files
    var audioFile = fs.readdirSync(audioFilePath)
        .slice(-1)[0]
        .replace('.wav', '.mp3');

Many thanks!

Upvotes: 32

Views: 39670

Answers (13)

pguardiario
pguardiario

Reputation: 55012

Another approach:

const fs = require('fs')
const glob = require('glob')

const newestFile = glob.sync('input/*xlsx')
  .map(name => ({name, ctime: fs.statSync(name).ctime}))
  .sort((a, b) => b.ctime - a.ctime)[0].name

Upvotes: 10

Alexander Mills
Alexander Mills

Reputation: 100506

This is a commonplace need - write files out to a temp dir and automatically open the most recent one.

The following works with node version 16:

#!/usr/bin/env node

const fs = require('fs');
const path = require('path');
const cp = require('child_process');
const fsPromises = fs.promises;

process.on('exit', code => {
    console.log('exiting with code:', code);
});

const folder = path.join(process.env.HOME, 'publications/temp');
const c = cp.spawn('sh');

const files = fs.readdirSync(folder).map(v => path.resolve(folder + '/' + v));

const openFile = (file) => {
    c.stdin.end(`(open "${file}" &> /dev/null) &> /dev/null &`);
};


if(files.length > 500) {
    console.error('too many files, clean this folder up lol');
    process.exit(1);
}

const newest = {file: null, mtime: null};

Promise.all(files.map(f => {
    return fsPromises.stat(f).then(stats => {
            if (!newest.file || (stats.mtime.getTime() > newest.mtime.getTime())) {
                newest.file= f;
                newest.mtime= stats.mtime;
            }
    });
})).then(v => {

    if(!newest.file){
        console.error('could not find the newest file?!');
        return;
    }

   openFile(newest.file);

});

you may want to check for folders instead of files, and you could add something like this towards the beginning:

     if (files.length === 1) {
        if (fs.statSync(files[0]).isFile()) {
            openFile(files[0]);
            process.exit(0);
        } else {
            console.error('folder or symlink where file should be?');
            process.exit(1);
        }
    }

Upvotes: 0

Hashbrown
Hashbrown

Reputation: 13023

An async version of @pguardiario's functional answer (I did this myself then found theirs halfway down the page when I went to add this).

import {promisify} from  'util';
import _glob       from  'glob';
const glob = promisify(_glob);

const newestFile = (await Promise.all(
    (await glob(YOUR_GLOB)).map(async (file) => (
        {file, mtime:(await fs.stat(file)).mtime}
    ))
))
    .sort(({mtime:a}, {mtime:b}) => ((a < b) ? 1 : -1))
    [0]
    .file
;

Upvotes: 0

Thanwa Ch.
Thanwa Ch.

Reputation: 135

First, you need to order files (newest at the begin)

Then, get the first element of an array for the most recent file.

I have modified code from @mikeysee to avoid the path exception so that I use the full path to fix them.

The snipped codes of 2 functions are shown below.

const fs = require('fs');
const path = require('path');

const getMostRecentFile = (dir) => {
    const files = orderReccentFiles(dir);
    return files.length ? files[0] : undefined;
};

const orderReccentFiles = (dir) => {
    return fs.readdirSync(dir)
        .filter(file => fs.lstatSync(path.join(dir, file)).isFile())
        .map(file => ({ file, mtime: fs.lstatSync(path.join(dir, file)).mtime }))
        .sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
};

const dirPath = '<PATH>';
getMostRecentFile(dirPath)

Upvotes: 5

mikeysee
mikeysee

Reputation: 1763

A more functional version might look like:

import { readdirSync, lstatSync } from "fs";

const orderReccentFiles = (dir: string) =>
  readdirSync(dir)
    .filter(f => lstatSync(f).isFile())
    .map(file => ({ file, mtime: lstatSync(file).mtime }))
    .sort((a, b) => b.mtime.getTime() - a.mtime.getTime());

const getMostRecentFile = (dir: string) => {
  const files = orderReccentFiles(dir);
  return files.length ? files[0] : undefined;
};

Upvotes: 7

echopeak
echopeak

Reputation: 261

Surprisingly, there is no example in this questions that explicitly uses Array functions, functional programming.

Here is my take on getting the latest file of a directory in nodejs. By default, it will get the latest file by any extension. When passing the extension property, the function will return the latest file for that extension.

The advantage of this code is that its declarative and modular and easy to understand as oppose to using "logic branching/control flows", of course given you understand how these array functions work 😀

const fs = require('fs');
const path = require('path');
function getLatestFile({directory, extension}, callback){
  fs.readdir(directory, (_ , dirlist)=>{
    const latest = dirlist.map(_path => ({stat:fs.lstatSync(path.join(directory, _path)), dir:_path}))
      .filter(_path => _path.stat.isFile())
      .filter(_path => extension ? _path.dir.endsWith(`.${extension}`) : 1)
      .sort((a, b) => b.stat.mtime - a.stat.mtime)
      .map(_path => _path.dir);
    callback(latest[0]);
  });
}

getLatestFile({directory:process.cwd(), extension:'mp3'}, (filename=null)=>{
  console.log(filename);
});

Upvotes: 2

vdegenne
vdegenne

Reputation: 13306

Using pure JavaScript and easy to understand structure :

function getLatestFile(dirpath) {

  // Check if dirpath exist or not right here

  let latest;

  const files = fs.readdirSync(dirpath);
  files.forEach(filename => {
    // Get the stat
    const stat = fs.lstatSync(path.join(dirpath, filename));
    // Pass if it is a directory
    if (stat.isDirectory())
      return;

    // latest default to first file
    if (!latest) {
      latest = {filename, mtime: stat.mtime};
      return;
    }
    // update latest if mtime is greater than the current latest
    if (stat.mtime > latest.mtime) {
      latest.filename = filename;
      latest.mtime = stat.mtime;
    }
  });

  return latest.filename;
}

Upvotes: 2

Trevedhek
Trevedhek

Reputation: 4428

While not the most efficient approach, this should be conceptually straight forward:

var fs = require('fs'); //File System
var audioFilePath = 'C:/scanner/audio/'; //Location of recorded audio files
fs.readdir(audioFilePath, function(err, files) {
    if (err) { throw err; }
    var audioFile = getNewestFile(files, audioFilePath).replace('.wav', '.mp3');
    //process audioFile here or pass it to a function...
    console.log(audioFile);
});

function getNewestFile(files, path) {
    var out = [];
    files.forEach(function(file) {
        var stats = fs.statSync(path + "/" +file);
        if(stats.isFile()) {
            out.push({"file":file, "mtime": stats.mtime.getTime()});
        }
    });
    out.sort(function(a,b) {
        return b.mtime - a.mtime;
    })
    return (out.length>0) ? out[0].file : "";
}

BTW, there is no obvious reason in the original post to use synchronous file listing.

Upvotes: 7

Joseph Gabriel
Joseph Gabriel

Reputation: 8520

[Extended umair's answer to correct a bug with current working directory]

function getNewestFile(dir, regexp) {
    var fs = require("fs"),
     path = require('path'),
    newest = null,
    files = fs.readdirSync(dir),
    one_matched = 0,
    i

    for (i = 0; i < files.length; i++) {

        if (regexp.test(files[i]) == false)
            continue
        else if (one_matched == 0) {
            newest = files[i];
            one_matched = 1;
            continue
        }

        f1_time = fs.statSync(path.join(dir, files[i])).mtime.getTime()
        f2_time = fs.statSync(path.join(dir, newest)).mtime.getTime()
        if (f1_time > f2_time)
            newest[i] = files[i]  
    }

    if (newest != null)
        return (path.join(dir, newest))
    return null
}

Upvotes: 2

umair siddiqui
umair siddiqui

Reputation: 9

with synchronized version of read directory (fs.readdirSync) and file status (fs.statSync):

function getNewestFile(dir, regexp) {
    newest = null
    files = fs.readdirSync(dir)
    one_matched = 0

    for (i = 0; i < files.length; i++) {

        if (regexp.test(files[i]) == false)
            continue
        else if (one_matched == 0) {
            newest = files[i]
            one_matched = 1
            continue
        }

        f1_time = fs.statSync(files[i]).mtime.getTime()
        f2_time = fs.statSync(newest).mtime.getTime()
        if (f1_time > f2_time)
            newest[i] = files[i]  
    }

    if (newest != null)
        return (dir + newest)
    return null
}

you can call this function as follows:

var f = getNewestFile("./", new RegExp('.*\.mp3'))

Upvotes: 0

Ilia Barahovsky
Ilia Barahovsky

Reputation: 10498

Assuming availability of underscore (http://underscorejs.org/) and taking synchronous approach (which doesn't utilize the node.js strengths, but is easier to grasp):

var fs = require('fs'),
    path = require('path'),
    _ = require('underscore');

// Return only base file name without dir
function getMostRecentFileName(dir) {
    var files = fs.readdirSync(dir);

    // use underscore for max()
    return _.max(files, function (f) {
        var fullpath = path.join(dir, f);

        // ctime = creation time is used
        // replace with mtime for modification time
        return fs.statSync(fullpath).ctime;
    });
}

Upvotes: 29

orcaman
orcaman

Reputation: 6591

this should do the trick ("dir" is the directory you use fs.readdir over to get the "files" array):

function getNewestFile(dir, files, callback) {
    if (!callback) return;
    if (!files || (files && files.length === 0)) {
        callback();
    }
    if (files.length === 1) {
        callback(files[0]);
    }
    var newest = { file: files[0] };
    var checked = 0;
    fs.stat(dir + newest.file, function(err, stats) {
        newest.mtime = stats.mtime;
        for (var i = 0; i < files.length; i++) {
            var file = files[i];
            (function(file) {
                fs.stat(file, function(err, stats) {
                    ++checked;
                    if (stats.mtime.getTime() > newest.mtime.getTime()) {
                        newest = { file : file, mtime : stats.mtime };
                    }
                    if (checked == files.length) {
                        callback(newest);
                    }
                });
            })(dir + file);
        }
    });
 }

Upvotes: 3

Nathan Friedly
Nathan Friedly

Reputation: 8166

Unfortunately, I don't think the files are guaranteed to be in any particular order.

Instead, you'll need to call fs.stat (or fs.statSync) on each file to get the date it was last modified, then select the newest one once you have all of the dates.

Upvotes: 1

Related Questions