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);
});
});
});
};
問題はfor each loop statが変数path
を介してすべてのファイルに対して呼び出されることです。コールバックが呼び出されたとき、path
にはすでに別の値があるため、誤ったディレクトリにdive
sするか、誤ったファイルに対してaction
を呼び出します。
おそらく、この問題はfs.statSync
を使用して簡単に解決できますが、プロセスをブロックしているため、これは私が好む解決策ではありません。
var path = dir + "/" + file;
path
をローカル変数にするのを忘れました。これで、ループの後ろで変更されることはありません。
これには 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);
})
});
私が本当にこれを回答として投稿する必要があるかどうかはわかりませんが、あなたの便宜と他のユーザーのために、役立つと思われる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);
};
別の適切なライブラリは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
免責事項:私は作者です。
車輪を再発明しないでください-代わりにオープンソースを使用して貢献してください。次のいずれかを試してください。
これには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)