我在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);
});
});
});
};
问题在于,每个循环的 通过变量path
为每个文件调用stat。调用回调时,path
已经有另一个值,因此它dive
进入错误的目录或调用action
来查找错误的文件。
使用fs.statSync
可能很容易解决这个问题,但这不是我想要的解决方案,因为它阻止了这个过程。
答案 0 :(得分:14)
var path = dir + "/" + file;
您忘记将path
设为局部变量。现在它不会在循环中背后改变。
答案 1 :(得分:10)
使用node-dir。因为您需要对目录和文件执行单独的操作,所以我将使用node-dir为您提供2个简单的迭代器。
异步迭代目录及其子目录的文件,并将一组文件路径传递给回调。
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);
})
});
答案 2 :(得分:7)
不确定我是否应该将此作为答案发布,但为方便您和其他用户,这里是OP的重写版本,可能会有用。它提供:
代码:
/**
* 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);
};
答案 3 :(得分:7)
另一个合适的库是 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);
});
该库已有详细记录,并提供了许多常见用例的示例。 https://github.com/nspragg/filehound
免责声明:我是作者。
答案 4 :(得分:2)
不要重新发明轮子 - 使用并为开源做出贡献。请尝试以下方法之一:
答案 5 :(得分:0)
为此有一个NPM模块:
npm dree
它使您可以将目录树表示为字符串或对象以及几个自定义选项。使用其文件回调将使您实现目标。
示例:
const dree = require('dree');
const options = {
depth: 5, // If you want to stop after 5 directory levels
exclude: /dir_to_exclude/, // If you want to exclude some pahts with a regexp
extensions: [ 'txt', 'jpg' ] // If you want only some extensions
};
const fileCallback = function (file) {
action(file.path);
};
let tree;
// Do it synchronously
tree = dree.scan('./dir', options, fileCallback);
// Do it asynchronously (returns promise)
tree = await dree.scanAsync('./dir', options, fileCallback);
// Here tree contains an object representing the whole directory tree (filtered with options)
答案 6 :(得分:0)
为此有一个NPM模块:
npm dree
示例:
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)
答案 7 :(得分:-2)
function loop( ) {
var item = list.shift( );
if ( item ) {
// content of the loop
functionWithCallback( loop );
} else {
// after the loop has ended
whatever( );
}
}