关于使用fs.readdir进行异步目录搜索有什么想法吗?我意识到我们可以引入递归,并调用read目录函数来读取下一个目录,但我有点担心它不是异步的…
什么好主意吗?我已经看了node-walk,它很棒,但它不能像readdir那样只给我数组中的文件。虽然
寻找这样的输出…
['file1.txt', 'file2.txt', 'dir/file3.txt']
关于使用fs.readdir进行异步目录搜索有什么想法吗?我意识到我们可以引入递归,并调用read目录函数来读取下一个目录,但我有点担心它不是异步的…
什么好主意吗?我已经看了node-walk,它很棒,但它不能像readdir那样只给我数组中的文件。虽然
寻找这样的输出…
['file1.txt', 'file2.txt', 'dir/file3.txt']
当前回答
独立承诺实现
在这个例子中,我使用的是when.js承诺库。
var fs = require('fs')
, path = require('path')
, when = require('when')
, nodefn = require('when/node/function');
function walk (directory, includeDir) {
var results = [];
return when.map(nodefn.call(fs.readdir, directory), function(file) {
file = path.join(directory, file);
return nodefn.call(fs.stat, file).then(function(stat) {
if (stat.isFile()) { return results.push(file); }
if (includeDir) { results.push(file + path.sep); }
return walk(file, includeDir).then(function(filesInDir) {
results = results.concat(filesInDir);
});
});
}).then(function() {
return results;
});
};
walk(__dirname).then(function(files) {
console.log(files);
}).otherwise(function(error) {
console.error(error.stack || error);
});
我包含了一个可选参数includeDir,如果设置为true,它将在文件列表中包含目录。
其他回答
对于Node 10.3+,这里是一个For -await解决方案:
#!/usr/bin/env node
const FS = require('fs');
const Util = require('util');
const readDir = Util.promisify(FS.readdir);
const Path = require('path');
async function* readDirR(path) {
const entries = await readDir(path,{withFileTypes:true});
for(let entry of entries) {
const fullPath = Path.join(path,entry.name);
if(entry.isDirectory()) {
yield* readDirR(fullPath);
} else {
yield fullPath;
}
}
}
async function main() {
const start = process.hrtime.bigint();
for await(const file of readDirR('/mnt/home/media/Unsorted')) {
console.log(file);
}
console.log((process.hrtime.bigint()-start)/1000000n);
}
main().catch(err => {
console.error(err);
});
这种解决方案的好处是,您可以立即开始处理结果;例如,读取媒体目录中的所有文件需要12秒,但如果我这样做,我可以在几毫秒内得到第一个结果。
因为每个人都应该写自己的,所以我写了一个。
步行(dir, cb, endCb) cb(文件) 零endCb (err |)
脏
module.exports = walk;
function walk(dir, cb, endCb) {
var fs = require('fs');
var path = require('path');
fs.readdir(dir, function(err, files) {
if (err) {
return endCb(err);
}
var pending = files.length;
if (pending === 0) {
endCb(null);
}
files.forEach(function(file) {
fs.stat(path.join(dir, file), function(err, stats) {
if (err) {
return endCb(err)
}
if (stats.isDirectory()) {
walk(path.join(dir, file), cb, function() {
pending--;
if (pending === 0) {
endCb(null);
}
});
} else {
cb(path.join(dir, file));
pending--;
if (pending === 0) {
endCb(null);
}
}
})
});
});
}
谁想要一个公认答案的同步替代方案(我知道我做过):
var fs = require('fs');
var path = require('path');
var walk = function(dir) {
let results = [], err = null, list;
try {
list = fs.readdirSync(dir)
} catch(e) {
err = e.toString();
}
if (err) return err;
var i = 0;
return (function next() {
var file = list[i++];
if(!file) return results;
file = path.resolve(dir, file);
let stat = fs.statSync(file);
if (stat && stat.isDirectory()) {
let res = walk(file);
results = results.concat(res);
return next();
} else {
results.push(file);
return next();
}
})();
};
console.log(
walk("./")
)
我必须将基于promise的砂光器库添加到列表中。
var sander = require('sander');
sander.lsr(directory).then( filenames => { console.log(filenames) } );
There are basically two ways of accomplishing this. In an async environment you'll notice that there are two kinds of loops: serial and parallel. A serial loop waits for one iteration to complete before it moves onto the next iteration - this guarantees that every iteration of the loop completes in order. In a parallel loop, all the iterations are started at the same time, and one may complete before another, however, it is much faster than a serial loop. So in this case, it's probably better to use a parallel loop because it doesn't matter what order the walk completes in, just as long as it completes and returns the results (unless you want them in order).
一个平行循环看起来是这样的:
var fs = require('fs');
var path = require('path');
var walk = function(dir, done) {
var results = [];
fs.readdir(dir, function(err, list) {
if (err) return done(err);
var pending = list.length;
if (!pending) return done(null, results);
list.forEach(function(file) {
file = path.resolve(dir, file);
fs.stat(file, function(err, stat) {
if (stat && stat.isDirectory()) {
walk(file, function(err, res) {
results = results.concat(res);
if (!--pending) done(null, results);
});
} else {
results.push(file);
if (!--pending) done(null, results);
}
});
});
});
};
一个串行循环看起来像这样:
var fs = require('fs');
var path = require('path');
var walk = function(dir, done) {
var results = [];
fs.readdir(dir, function(err, list) {
if (err) return done(err);
var i = 0;
(function next() {
var file = list[i++];
if (!file) return done(null, results);
file = path.resolve(dir, file);
fs.stat(file, function(err, stat) {
if (stat && stat.isDirectory()) {
walk(file, function(err, res) {
results = results.concat(res);
next();
});
} else {
results.push(file);
next();
}
});
})();
});
};
并且在你的主目录中测试它(警告:如果你的主目录中有很多东西,结果列表将会非常大):
walk(process.env.HOME, function(err, results) {
if (err) throw err;
console.log(results);
});
编辑:改进的示例。