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
Walking a directory with Node.js
提问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 action
to 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, path
already has another value and so it dive
s into the wrong directories or calls the action
for 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 path
a 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( );
}
}