用Node.js遍历一个目录

我在node.js中有这个代码的问题。 我想recursion地遍历一个目录树,并将callbackaction到树中的每个文件。 这是我目前的代码:

 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); }); }); }); }; 

问题是在每个循环中 ,通过可变path为每个文件调用stat。 当callback被调用时, path已经有了另一个值,所以它会跳入错误的目录或调用错误文件的action

可能这个问题很容易通过使用fs.statSync来解决,但这不是我想要的解决scheme,因为它阻止了这个过程。

var path = dir + "/" + file;

你忘了把path变成局部variables。 现在它不会在循环后面改变。

使用节点目录为此。 因为你需要对目录和文件进行单独的操作,所以我会给你2个简单的使用node-dir的迭代器。

asynchronous迭代目录及其子目录的文件,并将文件path数组传递给callback函数。

 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); }) }); 

asynchronous迭代目录及其子目录的子目录,并将目录path数组传递给callback。

 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); }) }); 

不确定是否真的应该把这个作为答案,但为了方便和其他用户,这里是一个重写的OP版本,可能会certificate是有用的。 它提供:

  • 更好的错误pipe理支持
  • 探索完成时调用的全局完成callback

代码:

 /** * 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); }; 

另一个合适的库是filehound 。 它支持文件过滤(如果需要),callback和承诺。

例如:

 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); }); 

该图书馆是有据可查的,并提供了许多常见用例的例子。 https://github.com/nspragg/filehound

免责声明:我是作者。

不要重新发明轮子,而应该为开源贡献力量。 尝试以下方法之一:

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