我试图递归获取目录中存在的所有文件夹的列表。然后,我想获取每个文件夹中所有文件夹的列表。
我想继续递归执行此操作。我该怎么办?
const folder = 'folder1/';
const fs = require('fs');
fs.readdir(folder, (err, files) => {
files.forEach(file => {
// detect folder and continue to look for sub-folders
});
});
答案 0 :(得分:2)
没有错误检查或承诺,这是一种快速解决方案:
const folder = 'test/';
const fs = require('fs');
recurseFolder(folder);
function recurseFolder (folder_top) {
fs.readdir(folder_top, (err, things) => {
things.forEach(thing => {
const full_path = folder_top + thing;
fs.stat(full_path, (error, stat) => {
if (stat.isFile()) {
console.log(thing);
} else if (stat.isDirectory()) {
console.log(thing + "*");
recurseFolder(full_path + '/');
}
});
});
});
}
答案 1 :(得分:1)
这是一个使用Promises递归查找所有子目录的实现:
const fs = require('fs');
const path = require('path');
// Uncomment polyfill if using Node.js < 10.x
// const util = require('util');
// fs.promises = {
// readdir: util.promisify(fs.readdir),
// stat: util.promisify(fs.stat)
// };
async function listDirectories(dir) {
const fileNames = await fs.promises.readdir(dir);
// Create a directory listing
const listings = await Promise.all(
fileNames.map(async (name) => {
const file = path.join(dir, name);
const stat = await fs.promises.stat(file);
return {file, stat};
})
);
// Create a list of all the sub directories using the listings
const subDirs = listings.filter((listing) => listing.stat.isDirectory())
.map((listing) => listing.file);
// Recurse over the sub directories and add their sub directories to the list of directories
const subSubDirs = await Promise.all(subDirs.map(listDirectories));
return subSubDirs.reduce((a, b) => a.concat(b), subDirs);
}
listDirectories('folder1/').then((dirs) => {
console.log(dirs);
}).catch((err) => {
process.exitCode = 1;
console.error(err);
});
与等效的基于回调的解决方案相比,我认为基于promise的解决方案更易于理解并且更不易出错:
const fs = require('fs');
const path = require('path');
function listDirectories(dir, callback) {
fs.readdir(dir, (err, fileNames) => {
if (err) return callback(err);
if (!fileNames.length) return callback(null, []);
// We have to keep track of the remaining operations
let remaining = fileNames.length;
const subDirs = [];
fileNames.forEach((name) => {
const file = path.join(dir, name);
fs.stat(file, (err, stats) => {
if (err) return callback(err);
if (stats.isDirectory()) {
subDirs.push(file);
listDirectories(file, (err, subSubDirs) => {
if (err) return callback(err);
subDirs.push(...subSubDirs);
if (!--remaining) {
// We've gathered the sub dirs of this sub dir and this was the last file to check, all done.
callback(null, subDirs);
}
});
} else if (!--remaining) {
// File was not a dir and was the last file to check, all done.
callback(null, subDirs);
}
});
});
});
}
listDirectories('folder1/',(err, dirs) => {
if (err) {
process.exitCode = 1;
console.error(err);
} else {
console.log(dirs);
}
});
答案 2 :(得分:0)
-99级:回调地狱
这里令人窒息,我需要呼吸一下。通过使用fs.promises
和较新的async-await
语法,我们可以轻松了解-
// main.js
const { readdir, stat } =
require ('fs') .promises
const { join } =
require ('path')
const dirs = async (path = ".") =>
(await stat (path)) .isDirectory ()
? Promise
.all
( (await readdir (path))
.map (p => dirs (join (path, p)))
)
.then
( results =>
[] .concat (path, ...results)
)
: []
dirs (process.argv[2]) .then (console.log, console.error)
在终端中,我安装了一个示例软件包,然后在我们项目的目录中测试该程序-
$ npm install ramda
$ node main.js .
[ '.'
, 'node_modules'
, 'node_modules/ramda'
, 'node_modules/ramda/dist'
, 'node_modules/ramda/es'
, 'node_modules/ramda/es/internal'
, 'node_modules/ramda/src'
, 'node_modules/ramda/src/internal'
]
我们可以添加一个depth
参数,该参数控制dirs
重复出现的深度-
// main.js
const { readdir, stat } =
require ('fs') .promises
const { join } =
require ('path')
const dirs = async (path = ".", depth = Infinity) =>
(await stat (path)) .isDirectory ()
? depth === -1
? []
: Promise
.all
( (await readdir (path))
.map (p => dirs (join (path, p), depth - 1))
)
.then
( results =>
[] .concat (path, ...results)
)
: []
dirs (process.argv[2], process.argv[3]) .then (console.log, console.error)
再次在终端中进行测试,我们显示了各种路径和深度-
$ node main.js . 1
[ '.'
, 'node_modules'
]
$ node main.js . 2
[ '.'
, 'node_modules'
, 'node_modules/ramda'
]
$ node main.js node_modules/ 1
[ 'node_modules/'
, 'node_modules/ramda'
]
$ node main.js node_modules/ 2
[ 'node_modules/'
, 'node_modules/ramda'
, 'node_modules/ramda/dist'
, 'node_modules/ramda/es'
, 'node_modules/ramda/src'
]
进阶
这个问题与我回答here的另一个问题有关。那里的答案使用了多种技术来提供完整的解决方案,但接着显示可以提取和重用某些模式。
在上面的dirs
解决方案中,我们只有一个功能要检查,因此我们无法识别模式。但是,我将说明如何将另一个答案Parallel
中出现的可重用模块也应用于此dirs
-
// main.js
const { readdir, stat } =
require ('fs') .promises
const { join } =
require ('path')
const Parallel =
require ('./parallel')
const dirs = async (path = ".", depth = Infinity) =>
(await stat (path)) .isDirectory ()
? depth === -1
? []
: Parallel (readdir (path))
.flatMap (f => dirs (join (path, f), depth - 1))
.then (results => [ path, ...results ])
: []
dirs (process.argv[2], process.argv[3]) .then (console.log, console.error)
点击上面的链接,了解如何使用Parallel
完成类似的任务
答案 3 :(得分:-1)
您可以使用IsFile和IsDirectory
files.forEach(file => {
fs.stat(file, function (error, stat) {
if (stat.isFile()) // it's a file
if (stat.isDirectory()) // it's a directory
});
});