关于使用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']
当前回答
使用承诺(Q)以函数式风格解决此问题:
var fs = require('fs'),
fsPath = require('path'),
Q = require('q');
var walk = function (dir) {
return Q.ninvoke(fs, 'readdir', dir).then(function (files) {
return Q.all(files.map(function (file) {
file = fsPath.join(dir, file);
return Q.ninvoke(fs, 'lstat', file).then(function (stat) {
if (stat.isDirectory()) {
return walk(file);
} else {
return [file];
}
});
}));
}).then(function (files) {
return files.reduce(function (pre, cur) {
return pre.concat(cur);
});
});
};
它返回一个数组的promise,所以你可以这样使用它:
walk('/home/mypath').then(function (files) { console.log(files); });
其他回答
如果你想使用npm包,扳手是很好的选择。
var wrench = require("wrench");
var files = wrench.readdirSyncRecursive("directory");
wrench.readdirRecursive("directory", function (error, files) {
// live your dreams
});
编辑(2018): 作者在2015年弃用了这个包:
扳手.js已弃用,并且在相当长的一段时间内没有更新。我强烈建议使用fs-extra来执行任何额外的文件系统操作。
简单,基于异步承诺
const fs = require('fs/promises');
const getDirRecursive = async (dir) => {
try {
const items = await fs.readdir(dir);
let files = [];
for (const item of items) {
if ((await fs.lstat(`${dir}/${item}`)).isDirectory()) files = [...files, ...(await getDirRecursive(`${dir}/${item}`))];
else files.push({file: item, path: `${dir}/${item}`, parents: dir.split("/")});
}
return files;
} catch (e) {
return e
}
};
用法:await getDirRecursive("./public");
使用node-dir可以生成您想要的输出
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 can iterate that array
files.forEach(function(path) {
action(null, path);
})
});
这是我如何使用nodejs的fs。递归搜索目录的Readdir函数。
const fs = require('fs');
const mime = require('mime-types');
const readdirRecursivePromise = path => {
return new Promise((resolve, reject) => {
fs.readdir(path, (err, directoriesPaths) => {
if (err) {
reject(err);
} else {
if (directoriesPaths.indexOf('.DS_Store') != -1) {
directoriesPaths.splice(directoriesPaths.indexOf('.DS_Store'), 1);
}
directoriesPaths.forEach((e, i) => {
directoriesPaths[i] = statPromise(`${path}/${e}`);
});
Promise.all(directoriesPaths).then(out => {
resolve(out);
}).catch(err => {
reject(err);
});
}
});
});
};
const statPromise = path => {
return new Promise((resolve, reject) => {
fs.stat(path, (err, stats) => {
if (err) {
reject(err);
} else {
if (stats.isDirectory()) {
readdirRecursivePromise(path).then(out => {
resolve(out);
}).catch(err => {
reject(err);
});
} else if (stats.isFile()) {
resolve({
'path': path,
'type': mime.lookup(path)
});
} else {
reject(`Error parsing path: ${path}`);
}
}
});
});
};
const flatten = (arr, result = []) => {
for (let i = 0, length = arr.length; i < length; i++) {
const value = arr[i];
if (Array.isArray(value)) {
flatten(value, result);
} else {
result.push(value);
}
}
return result;
};
假设在节点项目根目录中有一个名为“/database”的路径。一旦这个承诺被解决,它应该吐出'/database'下的每个文件的数组。
readdirRecursivePromise('database').then(out => {
console.log(flatten(out));
}).catch(err => {
console.log(err);
});
这是另一个实现。上述解决方案都没有任何限制,因此如果您的目录结构很大,它们都会崩溃并最终耗尽资源。
var async = require('async');
var fs = require('fs');
var resolve = require('path').resolve;
var scan = function(path, concurrency, callback) {
var list = [];
var walker = async.queue(function(path, callback) {
fs.stat(path, function(err, stats) {
if (err) {
return callback(err);
} else {
if (stats.isDirectory()) {
fs.readdir(path, function(err, files) {
if (err) {
callback(err);
} else {
for (var i = 0; i < files.length; i++) {
walker.push(resolve(path, files[i]));
}
callback();
}
});
} else {
list.push(path);
callback();
}
}
});
}, concurrency);
walker.push(path);
walker.drain = function() {
callback(list);
}
};
使用50的并发工作得非常好,并且几乎和小型目录结构的简单实现一样快。