Javascript 使用 Node.js 遍历目录

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7041638/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 00:23:59  来源:igfitidea点击:

Walking a directory with Node.js

javascriptnode.jsfilesystems

提问by pvorb

I've got a problem with this code in node.js. I want to recursively walk through a directory tree and apply the callback actionto every file in the tree. This is my code at the moment:

我在 node.js 中遇到了这段代码的问题。我想递归遍历目录树并将回调应用于树action中的每个文件。这是我目前的代码:

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

The problem is that in the for each loopstat is called for every file via the variable path. When the callback is called, pathalready has another value and so it dives into the wrong directories or calls the actionfor the wrong files.

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

Probably this problem could easily get solved by using fs.statSync, but this is not the solution I would prefer, since it is blocking the process.

使用 可能很容易解决这个问题fs.statSync,但这不是我喜欢的解决方案,因为它阻塞了进程。

采纳答案by Raynos

var path = dir + "/" + file;

var path = dir + "/" + file;

You forgot to make patha local variable. Now it won't be changed behind your back in the loop.

你忘了创建path一个局部变量。现在它不会在你的背后改变循环。

回答by Christiaan Westerbeek

Use node-dirfor this. Because you need a separate action for directories and files, I'll give you 2 simple iterators using node-dir.

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

Asynchronously iterate the files of a directory and its subdirectories and pass an array of file paths to a 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);
  })
});

Asynchronously iterate the subdirectories of a directory and its subdirectories and pass an array of directory paths to a 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);
  })
});

回答by nickool

Another suitable library is filehound. It supports file filtering (if required), callbacks and promises.

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

For example:

例如:

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

The library is well documented and provides numerous examples of common use cases. https://github.com/nspragg/filehound

该库文档齐全,并提供了许多常见用例的示例。 https://github.com/nspragg/filehound

Disclaimer: I'm the author.

免责声明:我是作者。

回答by Samuel Rossille

Not sure if I should really post this as an answer, but for your convenience and other users, here is a rewritten version of OP's which might prove useful. It provides:

不确定我是否真的应该将其作为答案发布,但为了您和其他用户的方便,这里是 OP 的重写版本,这可能会很有用。它提供:

  • Better error management support
  • A global completion callback which is called when the exploration is complete
  • 更好的错误管理支持
  • 探索完成时调用的全局完成回调

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

回答by B T

Don't reinvent the wheel - use and contribute to open source instead. Try one of the following:

不要重新发明轮子 - 而是使用开源并为开源做出贡献。尝试以下方法之一:

回答by EuberDeveloper

There is an NPM module for this:

有一个 NPM 模块:

npm dree

npm dree

Example:

例子:

const dree = require('dree');
const options = {
    depth: 5,                        // To stop after 5 directory levels
    exclude: /dir_to_exclude/,       // To exclude some pahts with a regexp
    extensions: [ 'txt', 'jpg' ]     // To include only some extensions
};

const fileCallback = function (file) {
    action(file.path);
};

let tree;

// Doing it synchronously
tree = dree.scan('./dir', options, fileCallback);

// Doing it asynchronously (returns promise)
tree = await dree.scanAsync('./dir', options, fileCallback);

// Here tree contains an object representing the whole directory tree (filtered with options)

回答by xavierm02

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