Javascript 使用Node.js遍历目录

Javascript 使用Node.js遍历目录,javascript,node.js,filesystems,Javascript,Node.js,Filesystems,node.js中的代码有问题。我希望递归地遍历目录树,并将回调操作应用于树中的每个文件。这是我目前的代码: 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) { }; //

node.js中的代码有问题。我希望递归地遍历目录树,并将回调
操作
应用于树中的每个文件。这是我目前的代码:

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);
      });
    });
  });
};
问题是在for each循环中,通过变量
path
为每个文件调用stat。调用回调时,
path
已经有另一个值,因此它
跳入错误的目录或调用错误文件的
操作

也许这个问题可以通过使用
fs.statSync
轻松解决,但这不是我想要的解决方案,因为它阻塞了这个过程


您忘记将
path
设置为局部变量。现在,它不会在你背后的循环中被改变。

我不确定我是否真的应该将此作为一个答案发布,但为了你和其他用户的方便,这里有一个重写版本的OP,它可能会被证明是有用的。它规定:

function loop( ) {
    var item = list.shift( );
    if ( item ) {
        // content of the loop
        functionWithCallback( loop );
    } else {
        // after the loop has ended
        whatever( );
    }
}
  • 更好的错误管理支持
  • 当探测完成时调用的全局完成回调
守则:

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

不要重新发明轮子,而是使用开源。请尝试以下操作之一:

    • 用于此。因为您需要对目录和文件执行单独的操作,所以我将使用node dir为您提供两个简单的迭代器

      异步迭代目录及其子目录的文件,并将文件路径数组传递给回调

      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);
        })
      });
      
      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);
        })
      });
      
      异步迭代目录及其子目录的子目录,并将目录路径数组传递给回调

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

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

      例如:

      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);
      });
      
      该库有很好的文档记录,并提供了许多常见用例的示例。


      免责声明:我是作者

      为此有一个NPM模块:

      npm

      例如:

      const dree=require('dree');
      常量选项={
      深度:5,//在5个目录级别之后停止
      排除:/dir_to_exclude/,//使用regexp排除某些PAHT
      扩展:['txt','jpg']//只包括一些扩展
      };
      const fileCallback=函数(文件){
      动作(file.path);
      };
      让树;
      //同步进行
      tree=dree.scan('./dir',options,fileCallback);
      //异步执行(返回承诺)
      tree=wait dree.scanAsync('./dir',options,fileCallback);
      //此处目录树包含一个表示整个目录树的对象(使用选项过滤)
      
      函数withcallback(循环)中,您希望我做什么还不清楚。你能把你的解决方案应用到我的例子中吗?对于列表中的每个元素,你想做些什么,这个函数做了,然后调用回调函数。我将编辑tomorow,使其更接近您的代码。这将通过放置
      “use strict”来捕获
      位于文件顶部。为了跨平台兼容性,请使用节点的函数,而不是附加原始的
      “/”
      。我不喜欢那个模块。(这是这个问题的结果)有一个更简单的api。这太棒了!一个小优化:在第一次挂起--。除非已完成当前所在的目录,否则无法处于pending==0。