Marcher un répertoire avec Node.js

J'ai un problème avec ce code dans node.js. Je veux parcourir récursivement un arbre de répertoires et appliquer l' action rappel à chaque fichier dans l'arborescence. Voici mon code pour le moment:

 var fs = require("fs"); // General function var dive = function (dir, action) { // Assert that it's a function if (typeof action !== "function") action = function (error, file) { }; // Read the directory fs.readdir(dir, function (err, list) { // Return the error if something went wrong if (err) return action(err); // For every file in the list list.forEach(function (file) { // Full path of that file path = dir + "/" + file; // Get the file's stats fs.stat(path, function (err, stat) { console.log(stat); // If the file is a directory if (stat && stat.isDirectory()) // Dive into the directory dive(path, action); else // Call the action action(null, path); }); }); }); }; 

Le problème est que dans le cas de chaque boucle, la stat est appelée pour chaque fichier via le path variable. Lorsque le rappel est appelé, le path déjà une autre valeur et donc il dive dans les mauvais répertoires ou appelle l' action pour les mauvais fichiers.

Probablement, ce problème pourrait facilement être résolu en utilisant fs.statSync , mais ce n'est pas la solution que je préfère, car elle bloque le processus.

var path = dir + "/" + file;

Vous avez oublié de faire du path une variable locale. Maintenant, il ne sera pas changé derrière votre dos en boucle.

Utilisez node-dir pour cela. Parce que vous avez besoin d'une action distincte pour les répertoires et les fichiers, je vais vous donner 2 itérateurs simples en utilisant node-dir.

Répétez de manière asynchrone les fichiers d'un répertoire et ses sous-répertoires et passe un ensemble de chemins de fichiers vers un rappel.

 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'll iterate that array files.forEach(function(filepath) { actionOnFile(null, filepath); }) }); 

Répétez de manière asynchrone les sous-répertoires d'un répertoire et de ses sous-répertoires et passe un tableau de chemins d'annuaire à un rappel.

 var dir = require('node-dir'); dir.subdirs(__dirname, function(err, subdirs) { if (err) throw err; console.log(subdirs); //we have an array of subdirs now, so now we'll iterate that array subdirs.forEach(function(filepath) { actionOnDir(null, filepath); }) }); 

Je ne sais pas si je devrais vraiment publier ceci comme une réponse, mais pour votre commodité et d'autres utilisateurs, voici une version réécrite de OP qui pourrait s'avérer utile. Il offre:

  • Meilleur support de gestion des erreurs
  • Un rappel d'achèvement global qui s'appelle lorsque l'exploration est terminée

Le code:

 /** * dir: path to the directory to explore * action(file, stat): called on each file or until an error occurs. file: path to the file. stat: stat of the file (retrived by fs.stat) * done(err): called one time when the process is complete. err is undifined is everything was ok. the error that stopped the process otherwise */ var walk = function(dir, action, done) { // this flag will indicate if an error occured (in this case we don't want to go on walking the tree) var dead = false; // this flag will store the number of pending async operations var pending = 0; var fail = function(err) { if(!dead) { dead = true; done(err); } }; var checkSuccess = function() { if(!dead && pending == 0) { done(); } }; var performAction = function(file, stat) { if(!dead) { try { action(file, stat); } catch(error) { fail(error); } } }; // this function will recursively explore one directory in the context defined by the variables above var dive = function(dir) { pending++; // async operation starting after this line fs.readdir(dir, function(err, list) { if(!dead) { // if we are already dead, we don't do anything if (err) { fail(err); // if an error occured, let's fail } else { // iterate over the files list.forEach(function(file) { if(!dead) { // if we are already dead, we don't do anything var path = dir + "/" + file; pending++; // async operation starting after this line fs.stat(path, function(err, stat) { if(!dead) { // if we are already dead, we don't do anything if (err) { fail(err); // if an error occured, let's fail } else { if (stat && stat.isDirectory()) { dive(path); // it's a directory, let's explore recursively } else { performAction(path, stat); // it's not a directory, just perform the action } pending--; checkSuccess(); // async operation complete } } }); } }); pending--; checkSuccess(); // async operation complete } } }); }; // start exploration dive(dir); }; 

Une autre bibliothèque appropriée est filehound . Il prend en charge le filtrage des fichiers (si nécessaire), les rappels et les promesses.

Par exemple:

 const Filehound = require('filehound'); function action(file) { console.log(`process ${file}`) } Filehound.create() .find((err, files) => { if (err) { return console.error(`error: ${err}`); } files.forEach(action); }); 

La bibliothèque est bien documentée et fournit de nombreux exemples de cas d'usage courant. https://github.com/nspragg/filehound

Avertissement: je suis l'auteur.

Ne réinventez pas la roue – utilisez et contribuez à la source ouverte. Essayez l'une des options suivantes:

 function loop( ) { var item = list.shift( ); if ( item ) { // content of the loop functionWithCallback( loop ); } else { // after the loop has ended whatever( ); } }