web-dev-qa-db-fra.com

node.js fs.readdir recherche de répertoire récursive

Des idées sur une recherche dans un répertoire async utilisant fs.readdir? Je me rends compte que nous pourrions introduire la récursivité et appeler la fonction read directory avec le prochain répertoire à lire, mais je suis un peu inquiète que cela ne soit pas async ...

Des idées? J'ai regardé node-walk ce qui est génial, mais ne me donne pas uniquement les fichiers d'un tableau, comme le fait readdir. Bien que

Vous cherchez une sortie comme ...

['file1.txt', 'file2.txt', 'dir/file3.txt']
235
crawf

Il y a fondamentalement deux façons d'accomplir cela. Dans un environnement asynchrone, vous remarquerez qu'il existe deux types de boucles: série et parallèle. Une boucle série attend la fin d'une itération avant de passer à la prochaine itération - cela garantit que chaque itération de la boucle se termine dans l'ordre. Dans une boucle parallèle, toutes les itérations sont démarrées en même temps et une peut se terminer avant une autre, mais elle est beaucoup plus rapide qu'une boucle en série. Donc, dans ce cas, il est probablement préférable d’utiliser une boucle parallèle car l’ordre dans lequel la marche est terminée n’a aucune importance, tant qu’elle complète et renvoie les résultats (à moins que vous ne les vouliez dans l’ordre).

Une boucle parallèle ressemblerait à ceci:

var fs = require('fs');
var path = require('path');
var walk = function(dir, done) {
  var results = [];
  fs.readdir(dir, function(err, list) {
    if (err) return done(err);
    var pending = list.length;
    if (!pending) return done(null, results);
    list.forEach(function(file) {
      file = path.resolve(dir, file);
      fs.stat(file, function(err, stat) {
        if (stat && stat.isDirectory()) {
          walk(file, function(err, res) {
            results = results.concat(res);
            if (!--pending) done(null, results);
          });
        } else {
          results.Push(file);
          if (!--pending) done(null, results);
        }
      });
    });
  });
};

Une boucle série ressemblerait à ceci:

var fs = require('fs');
var walk = function(dir, done) {
  var results = [];
  fs.readdir(dir, function(err, list) {
    if (err) return done(err);
    var i = 0;
    (function next() {
      var file = list[i++];
      if (!file) return done(null, results);
      file = dir + '/' + file;
      fs.stat(file, function(err, stat) {
        if (stat && stat.isDirectory()) {
          walk(file, function(err, res) {
            results = results.concat(res);
            next();
          });
        } else {
          results.Push(file);
          next();
        }
      });
    })();
  });
};

Et pour le tester sur votre répertoire personnel (ATTENTION: la liste des résultats sera énorme si vous avez beaucoup de choses dans votre répertoire personnel):

walk(process.env.HOME, function(err, results) {
  if (err) throw err;
  console.log(results);
});

EDIT: exemples améliorés.

358
chjj

Juste au cas où quelqu'un le jugerait utile, j'ai également mis au point une version synchrone.

var walk = function(dir) {
    var results = [];
    var list = fs.readdirSync(dir);
    list.forEach(function(file) {
        file = dir + '/' + file;
        var stat = fs.statSync(file);
        if (stat && stat.isDirectory()) { 
            /* Recurse into a subdirectory */
            results = results.concat(walk(file));
        } else { 
            /* Is a file */
            results.Push(file);
        }
    });
    return results;
}

Conseil: pour utiliser moins de ressources lors du filtrage. Filtrer dans cette fonction elle-même. Par exemple. Remplacez results.Push(file); par le code ci-dessous. Ajustez au besoin:

    file_type = file.split(".").pop();
    file_name = file.split(/(\\|\/)/g).pop();
    if (file_type == "json") results.Push(file);
93
Victor Powell

A. Regardez le module de fichier . Il a une fonction appelée marcher:

file.walk (démarrage, rappel)

Navigue dans une arborescence de fichiers, appelle des rappels pour chaque répertoire et passe (null, dirPath, dirs, fichiers).

Cela peut être pour vous! Et oui, c'est asynchrone. Cependant, je pense que vous auriez à regrouper vous-même le chemin complet, si vous en aviez besoin.

B. Une alternative, et même l'un de mes favoris: utilisez l'unix find pour cela. Pourquoi refaire quelque chose qui a déjà été programmé? Peut-être pas exactement ce dont vous avez besoin, mais ça vaut le coup de vérifier:

var execFile = require('child_process').execFile;
execFile('find', [ 'somepath/' ], function(err, stdout, stderr) {
  var file_list = stdout.split('\n');
  /* now you've got a list with full path file names */
});

Find a un mécanisme de mise en cache intégré dans Nice qui rend les recherches ultérieures très rapides, à condition que seuls quelques dossiers aient été modifiés.

Celui-ci utilise le maximum de nouvelles fonctionnalités à la mode disponibles dans le nœud 8, notamment Promises, util/promisify, déstructuration, asynchronisation, map + réduction et bien plus, ce qui incite vos collègues à se gratter la tête lorsqu'ils essaient qui se passe.

Noeud 8+

Aucune dépendance externe.

const { promisify } = require('util');
const { resolve } = require('path');
const fs = require('fs');
const readdir = promisify(fs.readdir);
const stat = promisify(fs.stat);

async function getFiles(dir) {
  const subdirs = await readdir(dir);
  const files = await Promise.all(subdirs.map(async (subdir) => {
    const res = resolve(dir, subdir);
    return (await stat(res)).isDirectory() ? getFiles(res) : res;
  }));
  return files.reduce((a, f) => a.concat(f), []);
}

Usage

getFiles(__dirname)
  .then(files => console.log(files))
  .catch(e => console.error(e));

Noeud 10.10+

Mise à jour pour le noeud 10+ avec encore plus de whizbang:

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

async function getFiles(dir) {
  const dirents = await readdir(dir, { withFileTypes: true });
  const files = dirents.map((dirent) => {
    const res = resolve(dir, dirent.name);
    return dirent.isDirectory() ? getFiles(res) : res;
  });
  return Array.prototype.concat(...files);
}

Notez que depuis le noeud 11.15.0, vous pouvez utiliser files.flat() au lieu de Array.prototype.concat(...files) pour aplatir le tableau de fichiers.

Noeud 11+

Si vous voulez que tout le monde soit complètement bouché, vous pouvez utiliser la version suivante en utilisant des itérateurs asynchrones . En plus d'être vraiment cool, il permet également aux consommateurs d'obtenir des résultats un par un, ce qui le rend plus adapté aux annuaires de très grande taille.

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

async function* getFiles(dir) {
  const dirents = await readdir(dir, { withFileTypes: true });
  for (const dirent of dirents) {
    const res = resolve(dir, dirent.name);
    if (dirent.isDirectory()) {
      yield* getFiles(res);
    } else {
      yield res;
    }
  }
}

L'utilisation a changé car le type de retour est maintenant un itérateur asynchrone au lieu d'une promesse

(async () => {
  for await (const f of getFiles('.')) {
    console.log(f);
  }
})()

Si quelqu'un est intéressé, j'ai écrit plus d'informations sur les itérateurs asynchrones ici: https://qwtel.com/posts/software/async-generators-in-the-wild/

76
qwtel

Un autre paquet Nice npm est glob .

npm install glob

Il est très puissant et devrait couvrir tous vos besoins récurrents.

Modifier:

En fait, je n'étais pas parfaitement satisfait de glob, j'ai donc créé readdirp .

Je suis convaincu que son API facilite la recherche récursive de fichiers et de répertoires et l’application de filtres spécifiques très facilement.

Lisez sa documentation pour avoir une meilleure idée de ce qu’il fait et installez-le via:

npm install readdirp

38
Thorsten Lorenz

Je recommande d'utiliser node-glob pour accomplir cette tâche.

var glob = require( 'glob' );  

glob( 'dirname/**/*.js', function( err, files ) {
  console.log( files );
});
26
Diogo Cardoso

Si vous voulez utiliser un paquet npm, clé est plutôt bon.

var wrench = require("wrench");

var files = wrench.readdirSyncRecursive("directory");

wrench.readdirRecursive("directory", function (error, files) {
    // live your dreams
});

EDIT (2018):
Toute personne lisant récemment: L'auteur a déconseillé d'utiliser ce package en 2015:

wrench.js est obsolète et n'a pas été mis à jour depuis un certain temps. Je recommande fortement d'utiliser fs-extra pour effectuer des opérations supplémentaires sur le système de fichiers.

15
Domenic

J'ai aimé la réponse de chjj ci-dessus et je n'aurais pas pu créer ma version de la boucle parallèle sans ce début.

var fs = require("fs");

var tree = function(dir, done) {
  var results = {
        "path": dir
        ,"children": []
      };
  fs.readdir(dir, function(err, list) {
    if (err) { return done(err); }
    var pending = list.length;
    if (!pending) { return done(null, results); }
    list.forEach(function(file) {
      fs.stat(dir + '/' + file, function(err, stat) {
        if (stat && stat.isDirectory()) {
          tree(dir + '/' + file, function(err, res) {
            results.children.Push(res);
            if (!--pending){ done(null, results); }
          });
        } else {
          results.children.Push({"path": dir + "/" + file});
          if (!--pending) { done(null, results); }
        }
      });
    });
  });
};

module.exports = tree;

J'ai créé n Gist aussi. Commentaires bienvenus. Je commence toujours dans le domaine de NodeJS, donc j'espère en apprendre davantage.

9
kalisjoshua

Utilisez node-dir pour produire exactement le résultat souhaité

var dir = require('node-dir');

dir.files(__dirname, function(err, files) {
  if (err) throw err;
  console.log(files);
  //we have an array of files now, so now we can iterate that array
  files.forEach(function(path) {
    action(null, path);
  })
});
8

Avec récursion

var fs = require('fs')
var path = process.cwd()
var files = []

var getFiles = function(path, files){
    fs.readdirSync(path).forEach(function(file){
        var subpath = path + '/' + file;
        if(fs.lstatSync(subpath).isDirectory()){
            getFiles(subpath, files);
        } else {
            files.Push(path + '/' + file);
        }
    });     
}

Appel

getFiles(path, files)
console.log(files) // will log all files in directory
7
Loourr

J'ai codé cela récemment, et j'ai pensé qu'il serait logique de partager cela ici. Le code utilise = bibliothèque asynchrone .

var fs = require('fs');
var async = require('async');

var scan = function(dir, suffix, callback) {
  fs.readdir(dir, function(err, files) {
    var returnFiles = [];
    async.each(files, function(file, next) {
      var filePath = dir + '/' + file;
      fs.stat(filePath, function(err, stat) {
        if (err) {
          return next(err);
        }
        if (stat.isDirectory()) {
          scan(filePath, suffix, function(err, results) {
            if (err) {
              return next(err);
            }
            returnFiles = returnFiles.concat(results);
            next();
          })
        }
        else if (stat.isFile()) {
          if (file.indexOf(suffix, file.length - suffix.length) !== -1) {
            returnFiles.Push(filePath);
          }
          next();
        }
      });
    }, function(err) {
      callback(err, returnFiles);
    });
  });
};

Vous pouvez l'utiliser comme ceci:

scan('/some/dir', '.ext', function(err, files) {
  // Do something with files that ends in '.ext'.
  console.log(files);
});
4
recidive

En utilisant async/wait, cela devrait fonctionner:

const FS = require('fs');
const readDir = promisify(FS.readdir);
const fileStat = promisify(FS.stat);

async function getFiles(dir) {
    let files = await readDir(dir);

    let result = files.map(file => {
        let path = Path.join(dir,file);
        return fileStat(path).then(stat => stat.isDirectory() ? getFiles(path) : path);
    });

    return flatten(await Promise.all(result));
}

function flatten(arr) {
    return Array.prototype.concat(...arr);
}

Vous pouvez utiliser bluebird.Promisify ou ceci:

/**
 * Returns a function that will wrap the given `nodeFunction`. Instead of taking a callback, the returned function will return a promise whose fate is decided by the callback behavior of the given node function. The node function should conform to node.js convention of accepting a callback as last argument and calling that callback with error as the first argument and success value on the second argument.
 *
 * @param {Function} nodeFunction
 * @returns {Function}
 */
module.exports = function promisify(nodeFunction) {
    return function(...args) {
        return new Promise((resolve, reject) => {
            nodeFunction.call(this, ...args, (err, data) => {
                if(err) {
                    reject(err);
                } else {
                    resolve(data);
                }
            })
        });
    };
};

Le nœud 8+ a Promisify intégré

Voir mon autre réponse pour une approche génératrice capable de donner des résultats encore plus rapidement.

4
mpen

Une bibliothèque appelée Filehound est une autre option. Il va rechercher récursivement un répertoire donné (répertoire de travail par défaut). Il prend en charge divers filtres, rappels, promesses et recherches de synchronisation.

Par exemple, recherchez tous les fichiers dans le répertoire de travail actuel (à l'aide de rappels):

const Filehound = require('filehound');

Filehound.create()
.find((err, files) => {
    if (err) {
        return console.error(`error: ${err}`);
    }
    console.log(files); // array of files
});

Ou promet et en spécifiant un répertoire spécifique:

const Filehound = require('filehound');

Filehound.create()
.paths("/tmp")
.find()
.each(console.log);

Consultez la documentation pour d'autres cas d'utilisation et exemples d'utilisation: https://github.com/nspragg/filehound

Disclaimer: je suis l'auteur.

4
nickool

Découvrez la bibliothèque final-fs . Il fournit une fonction readdirRecursive:

ffs.readdirRecursive(dirPath, true, 'my/initial/path')
    .then(function (files) {
        // in the `files` variable you've got all the files
    })
    .otherwise(function (err) {
        // something went wrong
    });
3

Mise en œuvre de la promesse autonome

J'utilise la bibliothèque de promesses when.js dans cet exemple.

var fs = require('fs')
, path = require('path')
, when = require('when')
, nodefn = require('when/node/function');

function walk (directory, includeDir) {
    var results = [];
    return when.map(nodefn.call(fs.readdir, directory), function(file) {
        file = path.join(directory, file);
        return nodefn.call(fs.stat, file).then(function(stat) {
            if (stat.isFile()) { return results.Push(file); }
            if (includeDir) { results.Push(file + path.sep); }
            return walk(file, includeDir).then(function(filesInDir) {
                results = results.concat(filesInDir);
            });
        });
    }).then(function() {
        return results;
    });
};

walk(__dirname).then(function(files) {
    console.log(files);
}).otherwise(function(error) {
    console.error(error.stack || error);
});

J'ai inclus un paramètre facultatif includeDir qui inclura des répertoires dans la liste de fichiers s'il est défini sur true.

2
JayQuerie.com

klaw et klaw-sync valent la peine d'être considérés pour ce genre de chose. Ces faisaient partie de node-fs-extra .

2
dat

Voici encore un autre implémentation. Aucune des solutions ci-dessus n’a de limiteur. Par conséquent, si votre structure de répertoires est volumineuse, elles vont toutes se débattre et finir par manquer de ressources.

var async = require('async');
var fs = require('fs');
var resolve = require('path').resolve;

var scan = function(path, concurrency, callback) {
    var list = [];

    var walker = async.queue(function(path, callback) {
        fs.stat(path, function(err, stats) {
            if (err) {
                return callback(err);
            } else {
                if (stats.isDirectory()) {
                    fs.readdir(path, function(err, files) {
                        if (err) {
                            callback(err);
                        } else {
                            for (var i = 0; i < files.length; i++) {
                                walker.Push(resolve(path, files[i]));
                            }
                            callback();
                        }
                    });
                } else {
                    list.Push(path);
                    callback();
                }
            }
        });
    }, concurrency);

    walker.Push(path);

    walker.drain = function() {
        callback(list);
    }
};

L'utilisation d'une simultanéité de 50 fonctionne plutôt bien et est presque aussi rapide que des implémentations plus simples pour de petites structures de répertoires.

1
Monkey Boson

Utiliser Promises ( Q ) pour résoudre ce problème dans un style fonctionnel:

var fs = require('fs'),
    fsPath = require('path'),
    Q = require('q');

var walk = function (dir) {
  return Q.ninvoke(fs, 'readdir', dir).then(function (files) {

    return Q.all(files.map(function (file) {

      file = fsPath.join(dir, file);
      return Q.ninvoke(fs, 'lstat', file).then(function (stat) {

        if (stat.isDirectory()) {
          return walk(file);
        } else {
          return [file];
        }
      });
    }));
  }).then(function (files) {
    return files.reduce(function (pre, cur) {
      return pre.concat(cur);
    });
  });
};

Il retourne la promesse d'un tableau, vous pouvez donc l'utiliser comme:

walk('/home/mypath').then(function (files) { console.log(files); });
1
Gunar Gessner

J'ai modifié la réponse de Trevor Senior basée sur la promesse pour travailler avec Bluebird

var fs = require('fs'),
    path = require('path'),
    Promise = require('bluebird');

var readdirAsync = Promise.promisify(fs.readdir);
var statAsync = Promise.promisify(fs.stat);
function walkFiles (directory) {
    var results = [];
    return readdirAsync(directory).map(function(file) {
        file = path.join(directory, file);
        return statAsync(file).then(function(stat) {
            if (stat.isFile()) {
                return results.Push(file);
            }
            return walkFiles(file).then(function(filesInDir) {
                results = results.concat(filesInDir);
            });
        });
    }).then(function() {
        return results;
    });
}

//use
walkDir(__dirname).then(function(files) {
    console.log(files);
}).catch(function(e) {
    console.error(e); {
});
1
Phil Mander

Le module recursive-readdir possède cette fonctionnalité.

1
thSoft

Je dois ajouter la bibliothèque ponceuse basée sur la promesse à la liste.

 var sander = require('sander');
 sander.lsr(directory).then( filenames => { console.log(filenames) } );
1
IvanSanchez

Pour le plaisir, voici une version basée sur les flux qui fonctionne avec la bibliothèque de flux highland.js. Il a été co-écrit par Victor Vu.

###
  directory >---m------> dirFilesStream >---------o----> out
                |                                 |
                |                                 |
                +--------< returnPipe <-----------+

  legend: (m)erge  (o)bserve

 + directory         has the initial file
 + dirListStream     does a directory listing
 + out               prints out the full path of the file
 + returnPipe        runs stat and filters on directories

###

_ = require('highland')
fs = require('fs')
fsPath = require('path')

directory = _(['someDirectory'])
mergePoint = _()
dirFilesStream = mergePoint.merge().flatMap((parentPath) ->
  _.wrapCallback(fs.readdir)(parentPath).sequence().map (path) ->
    fsPath.join parentPath, path
)
out = dirFilesStream
# Create the return pipe
returnPipe = dirFilesStream.observe().flatFilter((path) ->
  _.wrapCallback(fs.stat)(path).map (v) ->
    v.isDirectory()
)
# Connect up the merge point now that we have all of our streams.
mergePoint.write directory
mergePoint.write returnPipe
mergePoint.end()
# Release backpressure.  This will print files as they are discovered
out.each H.log
# Another way would be to queue them all up and then print them all out at once.
# out.toArray((files)-> console.log(files))
1
Michael Connor

Utilisation de bluebird promise.coroutine:

let promise = require('bluebird'),
    PC = promise.coroutine,
    fs = promise.promisifyAll(require('fs'));
let getFiles = PC(function*(dir){
    let files = [];
    let contents = yield fs.readdirAsync(dir);
    for (let i = 0, l = contents.length; i < l; i ++) {
        //to remove dot(hidden) files on MAC
        if (/^\..*/.test(contents[i])) contents.splice(i, 1);
    }
    for (let i = 0, l = contents.length; i < l; i ++) {
        let content = path.resolve(dir, contents[i]);
        let contentStat = yield fs.statAsync(content);
        if (contentStat && contentStat.isDirectory()) {
            let subFiles = yield getFiles(content);
            files = files.concat(subFiles);
        } else {
            files.Push(content);
        }
    }
    return files;
});
//how to use
//easy error handling in one place
getFiles(your_dir).then(console.log).catch(err => console.log(err));
1
alexcres

Async

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

const readdir = (p, done, a = [], i = 0) => fs.readdir(p, (e, d = []) =>
  d.map(f => readdir(a[a.Push(path.join(p, f)) - 1], () =>
    ++i == d.length && done(a), a)).length || done(a))

readdir(__dirname, console.log)

Sync

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

const readdirSync = (p, a = []) => {
  if (fs.statSync(p).isDirectory())
    fs.readdirSync(p).map(f => readdirSync(a[a.Push(path.join(p, f)) - 1], a))
  return a
}

console.log(readdirSync(__dirname))

lisible en async

function readdir (currentPath, done, allFiles = [], i = 0) {
  fs.readdir(currentPath, function (e, directoryFiles = []) {
    if (!directoryFiles.length)
      return done(allFiles)
    directoryFiles.map(function (file) {
      var joinedPath = path.join(currentPath, file)
      allFiles.Push(joinedPath)
      readdir(joinedPath, function () {
        i = i + 1
        if (i == directoryFiles.length)
          done(allFiles)}
      , allFiles)
    })
  })
}

readdir(__dirname, console.log)

Note: les deux versions suivront des liens symboliques (identiques à l'original fs.readdir)

0
Afanasii Kurakin

Encore une autre réponse, mais cette fois en utilisant TypeScript:

/**
 * Recursively walk a directory asynchronously and obtain all file names (with full path).
 *
 * @param dir Folder name you want to recursively process
 * @param done Callback function, returns all files with full path.
 * @param filter Optional filter to specify which files to include, 
 *   e.g. for json files: (f: string) => /.json$/.test(f)
 */
const walk = (
  dir: string,
  done: (err: Error | null, results ? : string[]) => void,
  filter ? : (f: string) => boolean
) => {
  let results: string[] = [];
  fs.readdir(dir, (err: Error, list: string[]) => {
    if (err) {
      return done(err);
    }
    let pending = list.length;
    if (!pending) {
      return done(null, results);
    }
    list.forEach((file: string) => {
      file = path.resolve(dir, file);
      fs.stat(file, (err2, stat) => {
        if (stat && stat.isDirectory()) {
          walk(file, (err3, res) => {
            if (res) {
              results = results.concat(res);
            }
            if (!--pending) {
              done(null, results);
            }
          }, filter);
        } else {
          if (typeof filter === 'undefined' || (filter && filter(file))) {
            results.Push(file);
          }
          if (!--pending) {
            done(null, results);
          }
        }
      });
    });
  });
};
0
Erik Vullings

Parce que tout le monde devrait écrire le sien, j'en ai fabriqué un.

walk (dir, cb, endCb) cb (fichier) endCb (err | null)

SALE

module.exports = walk;

function walk(dir, cb, endCb) {
  var fs = require('fs');
  var path = require('path');

  fs.readdir(dir, function(err, files) {
    if (err) {
      return endCb(err);
    }

    var pending = files.length;
    if (pending === 0) {
      endCb(null);
    }
    files.forEach(function(file) {
      fs.stat(path.join(dir, file), function(err, stats) {
        if (err) {
          return endCb(err)
        }

        if (stats.isDirectory()) {
          walk(path.join(dir, file), cb, function() {
            pending--;
            if (pending === 0) {
              endCb(null);
            }
          });
        } else {
          cb(path.join(dir, file));
          pending--;
          if (pending === 0) {
            endCb(null);
          }
        }
      })
    });

  });
}
0
vvo

Ceci est ma réponse. J'espère que ça peut aider quelqu'un.

Mon objectif est de faire en sorte que la routine de recherche puisse s'arrêter n'importe où et, pour un fichier trouvé, indique la profondeur relative par rapport au chemin d'origine.

var _fs = require('fs');
var _path = require('path');
var _defer = process.nextTick;

// next() will pop the first element from an array and return it, together with
// the recursive depth and the container array of the element. i.e. If the first
// element is an array, it'll be dug into recursively. But if the first element is
// an empty array, it'll be simply popped and ignored.
// e.g. If the original array is [1,[2],3], next() will return [1,0,[[2],3]], and
// the array becomes [[2],3]. If the array is [[[],[1,2],3],4], next() will return
// [1,2,[2]], and the array becomes [[[2],3],4].
// There is an infinity loop `while(true) {...}`, because I optimized the code to
// make it a non-recursive version.
var next = function(c) {
    var a = c;
    var n = 0;
    while (true) {
        if (a.length == 0) return null;
        var x = a[0];
        if (x.constructor == Array) {
            if (x.length > 0) {
                a = x;
                ++n;
            } else {
                a.shift();
                a = c;
                n = 0;
            }
        } else {
            a.shift();
            return [x, n, a];
        }
    }
}

// cb is the callback function, it have four arguments:
//    1) an error object if any exception happens;
//    2) a path name, may be a directory or a file;
//    3) a flag, `true` means directory, and `false` means file;
//    4) a zero-based number indicates the depth relative to the original path.
// cb should return a state value to tell whether the searching routine should
// continue: `true` means it should continue; `false` means it should stop here;
// but for a directory, there is a third state `null`, means it should do not
// Dig into the directory and continue searching the next file.
var ls = function(path, cb) {
    // use `_path.resolve()` to correctly handle '.' and '..'.
    var c = [ _path.resolve(path) ];
    var f = function() {
        var p = next(c);
        p && s(p);
    };
    var s = function(p) {
        _fs.stat(p[0], function(err, ss) {
            if (err) {
                // use `_defer()` to turn a recursive call into a non-recursive call.
                cb(err, p[0], null, p[1]) && _defer(f);
            } else if (ss.isDirectory()) {
                var y = cb(null, p[0], true, p[1]);
                if (y) r(p);
                else if (y == null) _defer(f);
            } else {
                cb(null, p[0], false, p[1]) && _defer(f);
            }
        });
    };
    var r = function(p) {
        _fs.readdir(p[0], function(err, files) {
            if (err) {
                cb(err, p[0], true, p[1]) && _defer(f);
            } else {
                // not use `Array.prototype.map()` because we can make each change on site.
                for (var i = 0; i < files.length; i++) {
                    files[i] = _path.join(p[0], files[i]);
                }
                p[2].unshift(files);
                _defer(f);
            }
        });
    }
    _defer(f);
};

var printfile = function(err, file, isdir, n) {
    if (err) {
        console.log('-->   ' + ('[' + n + '] ') + file + ': ' + err);
        return true;
    } else {
        console.log('... ' + ('[' + n + '] ') + (isdir ? 'D' : 'F') + ' ' + file);
        return true;
    }
};

var path = process.argv[2];
ls(path, printfile);
0
manbaum

Un autre simple et utile

function walkDir(root) {
    const stat = fs.statSync(root);

    if (stat.isDirectory()) {
        const dirs = fs.readdirSync(root).filter(item => !item.startsWith('.'));
        let results = dirs.map(sub => walkDir(`${root}/${sub}`));
        return [].concat(...results);
    } else {
        return root;
    }
}
0
clinyong

Pour Node 10.3 + , voici une solution pour-attendre :

#!/usr/bin/env node

const FS = require('fs');
const Util = require('util');
const readDir = Util.promisify(FS.readdir);
const Path = require('path');

async function* readDirR(path) {
    const entries = await readDir(path,{withFileTypes:true});
    for(let entry of entries) {
        const fullPath = Path.join(path,entry.name);
        if(entry.isDirectory()) {
            yield* readDirR(fullPath);
        } else {
            yield fullPath;
        }
    }
}

async function main() {
    const start = process.hrtime.bigint();
    for await(const file of readDirR('/mnt/home/media/Unsorted')) {
        console.log(file);
    }
    console.log((process.hrtime.bigint()-start)/1000000n);
}

main().catch(err => {
    console.error(err);
});

L'avantage de cette solution est que vous pouvez commencer à traiter les résultats immédiatement; par exemple. Il faut 12 secondes pour lire tous les fichiers de mon répertoire multimédia, mais si je le fais de cette façon, je pourrai obtenir le premier résultat en quelques millisecondes.

0
mpen

découvrez loaddir https://npmjs.org/package/loaddir

npm install loaddir

  loaddir = require('loaddir')

  allJavascripts = []
  loaddir({
    path: __dirname + '/public/javascripts',
    callback: function(){  allJavascripts.Push(this.relativePath + this.baseName); }
  })

Vous pouvez utiliser fileName au lieu de baseName si vous avez également besoin de l'extension.

Un avantage supplémentaire est qu'il surveillera également les fichiers et rappellera à nouveau. Il y a des tonnes d'options de configuration pour le rendre extrêmement flexible.

Je viens de refaire la gemme guard de Ruby en utilisant loaddir dans un court instant

0
Funkodebat

C'est ainsi que j'utilise la fonction nodejs fs.readdir pour rechercher récursivement un répertoire.

const fs = require('fs');
const mime = require('mime-types');
const readdirRecursivePromise = path => {
    return new Promise((resolve, reject) => {
        fs.readdir(path, (err, directoriesPaths) => {
            if (err) {
                reject(err);
            } else {
                if (directoriesPaths.indexOf('.DS_Store') != -1) {
                    directoriesPaths.splice(directoriesPaths.indexOf('.DS_Store'), 1);
                }
                directoriesPaths.forEach((e, i) => {
                    directoriesPaths[i] = statPromise(`${path}/${e}`);
                });
                Promise.all(directoriesPaths).then(out => {
                    resolve(out);
                }).catch(err => {
                    reject(err);
                });
            }
        });
    });
};
const statPromise = path => {
    return new Promise((resolve, reject) => {
        fs.stat(path, (err, stats) => {
            if (err) {
                reject(err);
            } else {
                if (stats.isDirectory()) {
                    readdirRecursivePromise(path).then(out => {
                        resolve(out);
                    }).catch(err => {
                        reject(err);
                    });
                } else if (stats.isFile()) {
                    resolve({
                        'path': path,
                        'type': mime.lookup(path)
                    });
                } else {
                    reject(`Error parsing path: ${path}`);
                }
            }
        });
    });
};
const flatten = (arr, result = []) => {
    for (let i = 0, length = arr.length; i < length; i++) {
        const value = arr[i];
        if (Array.isArray(value)) {
            flatten(value, result);
        } else {
            result.Push(value);
        }
    }
    return result;
};

Supposons que vous avez un chemin appelé '/ database' à la racine de vos projets de nœud. Une fois que cette promesse est résolue, il devrait cracher un tableau de chaque fichier sous '/ database'.

readdirRecursivePromise('database').then(out => {
    console.log(flatten(out));
}).catch(err => {
    console.log(err);
});
0
Jason Clay

Voici une méthode récursive pour obtenir tous les fichiers, y compris les sous-répertoires.

const FileSystem = require("fs");
const Path = require("path");

//...

function getFiles(directory) {
    directory = Path.normalize(directory);
    let files = FileSystem.readdirSync(directory).map((file) => directory + Path.sep + file);

    files.forEach((file, index) => {
        if (FileSystem.statSync(file).isDirectory()) {
            Array.prototype.splice.apply(files, [index, 1].concat(getFiles(file)));
        }
    });

    return files;
}
0
Daniel