获取目录nodejs中的所有目录
Posted
技术标签:
【中文标题】获取目录nodejs中的所有目录【英文标题】:Get all directories within directory nodejs 【发布时间】:2013-08-09 08:24:08 【问题描述】:我希望这会是一件简单的事情,但我找不到任何可以这样做的东西。
我只想获取给定文件夹/目录中的所有文件夹/目录。
例如:
<MyFolder>
|- SomeFolder
|- SomeOtherFolder
|- SomeFile.txt
|- SomeOtherFile.txt
|- x-directory
我希望得到一个数组:
["SomeFolder", "SomeOtherFolder", "x-directory"]
或者上面的路径,如果这是它的服务方式......
那么,是否已经存在可以执行上述操作的东西了?
【问题讨论】:
【参考方案1】:应该这样做:
CoffeeScript(同步)
fs = require 'fs'
getDirs = (rootDir) ->
files = fs.readdirSync(rootDir)
dirs = []
for file in files
if file[0] != '.'
filePath = "#rootDir/#file"
stat = fs.statSync(filePath)
if stat.isDirectory()
dirs.push(file)
return dirs
CoffeeScript(异步)
fs = require 'fs'
getDirs = (rootDir, cb) ->
fs.readdir rootDir, (err, files) ->
dirs = []
for file, index in files
if file[0] != '.'
filePath = "#rootDir/#file"
fs.stat filePath, (err, stat) ->
if stat.isDirectory()
dirs.push(file)
if files.length == (index + 1)
cb(dirs)
JavaScript(异步)
var fs = require('fs');
var getDirs = function(rootDir, cb)
fs.readdir(rootDir, function(err, files)
var dirs = [];
for (var index = 0; index < files.length; ++index)
var file = files[index];
if (file[0] !== '.')
var filePath = rootDir + '/' + file;
fs.stat(filePath, function(err, stat)
if (stat.isDirectory())
dirs.push(this.file);
if (files.length === (this.index + 1))
return cb(dirs);
.bind(index: index, file: file));
);
【讨论】:
虽然这是针对生产系统的,但您确实希望避免使用同步的fs
方法。
任何阅读此答案的新手请注意:这是 CoffeeScript,而不是 javascript(我的朋友困惑地给我发消息问为什么 JavaScript 突然有语义空白)。
@nicksweet 你能把它转换成 JS 吗?
这个答案有一些明显的问题:它没有任何错误处理; (回调签名应该是(err, dirs)
);它不会在点文件或文件夹存在的情况下回调;它容易受到所有比赛条件的影响;它可能会在检查完所有条目之前回调。
呃,人们需要停止诋毁同步 api。确定是否应使用同步版本并不取决于它是“生产”。此外,在没有上下文的情况下,重复 async api 更好而同步很糟糕的广告是不准确的。我希望 JS 社区停止发布这个。同步更简单(耶),但会阻塞消息循环(嘘)。因此,不要在您不想阻止的服务器中使用同步 api,但可以随意在构建脚本中使用它们,例如在无关紧要的地方。 【参考方案2】:
承诺
const promises: readdir = require('fs')
const getDirectories = async source =>
(await readdir(source, withFileTypes: true ))
.filter(dirent => dirent.isDirectory())
.map(dirent => dirent.name)
回调
const readdir = require('fs')
const getDirectories = (source, callback) =>
readdir(source, withFileTypes: true , (err, files) =>
if (err)
callback(err)
else
callback(
files
.filter(dirent => dirent.isDirectory())
.map(dirent => dirent.name)
)
)
同步返回
const readdirSync = require('fs')
const getDirectories = source =>
readdirSync(source, withFileTypes: true )
.filter(dirent => dirent.isDirectory())
.map(dirent => dirent.name)
【讨论】:
小心,您需要绝对路径才能获取文件统计信息。require('path').resolve(__dirname, file)
@pilau 它只是不适用于相对路径,这就是您需要对其进行规范化的原因。为此,您可以使用 path.resolve。
@rickysullivan:您需要遍历响应并对内部的每个文件夹使用 path.resolve(srcpath, foldername)
既然你用的是es6:const getDirectories = srcPath => fs.readdirSync(srcPath).filter(file => fs.statSync(path.join(srcPath, file)).isDirectory())
请注意,如果目录中有符号链接,则会中断。请改用lstatSync
。【参考方案3】:
使用路径列出目录。
function getDirectories(path)
return fs.readdirSync(path).filter(function (file)
return fs.statSync(path+'/'+file).isDirectory();
);
【讨论】:
【参考方案4】:还有一个异步版本的 getDirectories,为此您需要 async module:
var fs = require('fs');
var path = require('path');
var async = require('async'); // https://github.com/caolan/async
// Original function
function getDirsSync(srcpath)
return fs.readdirSync(srcpath).filter(function(file)
return fs.statSync(path.join(srcpath, file)).isDirectory();
);
function getDirs(srcpath, cb)
fs.readdir(srcpath, function (err, files)
if(err)
console.error(err);
return cb([]);
var iterator = function (file, cb)
fs.stat(path.join(srcpath, file), function (err, stats)
if(err)
console.error(err);
return cb(false);
cb(stats.isDirectory());
)
async.filter(files, iterator, cb);
);
【讨论】:
【参考方案5】:this answer 的 CoffeeScript 版本,带有适当的错误处理:
fs = require "fs"
join = require "path"
async = require "async"
get_subdirs = (root, callback)->
fs.readdir root, (err, files)->
return callback err if err
subdirs = []
async.each files,
(file, callback)->
fs.stat join(root, file), (err, stats)->
return callback err if err
subdirs.push file if stats.isDirectory()
callback null
(err)->
return callback err if err
callback null, subdirs
取决于async
或者,use a module for this! (所有东西都有模块。[需要引用])
【讨论】:
【参考方案6】:以防万一其他人从网络搜索中结束,并且 Grunt 已经在他们的依赖项列表中,这个问题的答案就变得微不足道了。这是我的解决方案:
/**
* Return all the subfolders of this path
* @param String parentFolderPath - valid folder path
* @param String glob ['/*'] - optional glob so you can do recursive if you want
* @returns String[] subfolder paths
*/
getSubfolders = (parentFolderPath, glob = '/*') =>
return grunt.file.expand(filter: 'isDirectory', parentFolderPath + glob);
【讨论】:
【参考方案7】:递归解
我来这里是为了寻找获取所有子目录及其所有子目录等的方法。在accepted answer 的基础上,我写了这个:
const fs = require('fs');
const path = require('path');
function flatten(lists)
return lists.reduce((a, b) => a.concat(b), []);
function getDirectories(srcpath)
return fs.readdirSync(srcpath)
.map(file => path.join(srcpath, file))
.filter(path => fs.statSync(path).isDirectory());
function getDirectoriesRecursive(srcpath)
return [srcpath, ...flatten(getDirectories(srcpath).map(getDirectoriesRecursive))];
【讨论】:
这正是我正在寻找的,它似乎工作得很好,除了第一个路径以外的每个路径都显示如下:“src\\pages\\partials\\buttons”而不是这个“ src/页面/部分/按钮”。我添加了这个肮脏的修复: var res = getDirectoriesRecursive(srcpath); res = res.map(function(x) return x.replace(/\\/g,"/") ); console.log(res); 一种不那么脏的方法是 path.normalize()。 nodejs.org/api/path.html#path_path_normalize_path 您正在返回父目录,这是不可取的。我会重构 getDirectoriesRecursive 以防止这种情况发生:if (recursive) return [srcpath, ...flatten(getDirectories(srcpath).map(getDirectoriesRecursive))]; else return [...flatten(getDirectories(srcpath).map(getDirectoriesRecursive))];
【参考方案8】:
或者,如果您能够使用外部库,您可以使用filehound
。它支持回调、承诺和同步调用。
使用承诺:
const Filehound = require('filehound');
Filehound.create()
.path("MyFolder")
.directory() // only search for directories
.find()
.then((subdirectories) =>
console.log(subdirectories);
);
使用回调:
const Filehound = require('filehound');
Filehound.create()
.path("MyFolder")
.directory()
.find((err, subdirectories) =>
if (err) return console.error(err);
console.log(subdirectories);
);
同步调用:
const Filehound = require('filehound');
const subdirectories = Filehound.create()
.path("MyFolder")
.directory()
.findSync();
console.log(subdirectories);
有关更多信息(和示例),请查看文档:https://github.com/nspragg/filehound
免责声明:我是作者。
【讨论】:
【参考方案9】:如果您需要使用所有async
版本。你可以有这样的东西。
记录目录长度,用它作为指标来判断所有异步统计任务是否完成。
如果异步统计任务完成,则所有文件统计都已检查,所以调用回调
这仅在 Node.js 是单线程时才有效,因为它假定没有两个异步任务会同时增加计数器。
'use strict';
var fs = require("fs");
var path = require("path");
var basePath = "./";
function result_callback(results)
results.forEach((obj) =>
console.log("isFile: " + obj.fileName);
console.log("fileName: " + obj.isFile);
);
;
fs.readdir(basePath, (err, files) =>
var results = [];
var total = files.length;
var finished = 0;
files.forEach((fileName) =>
// console.log(fileName);
var fullPath = path.join(basePath, fileName);
fs.stat(fullPath, (err, stat) =>
// this will work because Node.js is single thread
// therefore, the counter will not increment at the same time by two callback
finished++;
if (stat.isFile())
results.push(
fileName: fileName,
isFile: stat.isFile()
);
if (finished == total)
result_callback(results);
);
);
);
如您所见,这是一种“深度优先”的方法,这可能会导致回调地狱,而且它不是很“实用”。人们试图用 Promise 解决这个问题,方法是将异步任务包装到 Promise 对象中。
'use strict';
var fs = require("fs");
var path = require("path");
var basePath = "./";
function result_callback(results)
results.forEach((obj) =>
console.log("isFile: " + obj.fileName);
console.log("fileName: " + obj.isFile);
);
;
fs.readdir(basePath, (err, files) =>
var results = [];
var total = files.length;
var finished = 0;
var promises = files.map((fileName) =>
// console.log(fileName);
var fullPath = path.join(basePath, fileName);
return new Promise((resolve, reject) =>
// try to replace fullPath wil "aaa", it will reject
fs.stat(fullPath, (err, stat) =>
if (err)
reject(err);
return;
var obj =
fileName: fileName,
isFile: stat.isFile()
;
resolve(obj);
);
);
);
Promise.all(promises).then((values) =>
console.log("All the promise resolved");
console.log(values);
console.log("Filter out folder: ");
values
.filter((obj) => obj.isFile)
.forEach((obj) =>
console.log(obj.fileName);
);
, (reason) =>
console.log("Not all the promise resolved");
console.log(reason);
);
);
【讨论】:
好代码!但我认为它应该是 Promise.all 块中的“过滤掉文件:”,因为它正在检查它是否是一个文件并记录它。 :)【参考方案10】:使用fs、path模块可以得到文件夹。这个使用 Promise。如果你会得到填充,你可以将 isDirectory() 更改为 isFile() Nodejs--fs--fs.Stats。最后,你可以得到文件'name file'extname 等等在Nodejs---Path
var fs = require("fs"),
path = require("path");
//your <MyFolder> path
var p = "MyFolder"
fs.readdir(p, function (err, files)
if (err)
throw err;
//this can get all folder and file under <MyFolder>
files.map(function (file)
//return file or folder path, such as **MyFolder/SomeFile.txt**
return path.join(p, file);
).filter(function (file)
//use sync judge method. The file will add next files array if the file is directory, or not.
return fs.statSync(file).isDirectory();
).forEach(function (files)
//The files is array, so each. files is the folder name. can handle the folder.
console.log("%s", files);
);
);
【讨论】:
来自审核队列:我可以请求您在您的答案周围添加更多上下文。仅代码的答案很难理解。如果您可以在帖子中添加更多信息,这将对提问者和未来的读者都有帮助。【参考方案11】:使用 fs-extra,它承诺异步 fs 调用,以及新的 await async 语法:
const fs = require("fs-extra");
async function getDirectories(path)
let filesAndDirectories = await fs.readdir(path);
let directories = [];
await Promise.all(
filesAndDirectories.map(name =>
return fs.stat(path + name)
.then(stat =>
if(stat.isDirectory()) directories.push(name)
)
)
);
return directories;
let directories = await getDirectories("/")
【讨论】:
不适合我。遇到.git
文件时会抛出错误。【参考方案12】:
对于 node.js 版本 >= v10.13.0,如果 withFileTypes
选项设置为 true
,fs.readdirSync 将返回一个 fs.Dirent 对象数组。
所以你可以使用,
const fs = require('fs')
const directories = source => fs.readdirSync(source,
withFileTypes: true
).reduce((a, c) =>
c.isDirectory() && a.push(c.name)
return a
, [])
【讨论】:
好点,但.filter(c => c.isDirectory())
会比使用reduce()
更简单
是的,但是 filter 返回一个 fs.Dirent 对象数组,它们是目录。 OP 想要目录名称。
是的,我还是更喜欢.filter(c => c.isDirectory()).map(c => c.name)
而不是reduce
电话。
我明白你的意思。我想 SO 读者可以根据他们的用例来决定。我想说,与从磁盘读取的 I/O 相比,循环内存阵列的开销应该可以忽略不计,即使您正在从 SSD 读取,但像往常一样,如果真的关心,他们可以测量。
【参考方案13】:
另一种递归方法
感谢 Mayur 让我了解 withFileTypes
。我编写了以下代码以递归方式获取特定文件夹的文件。可以轻松修改为仅获取目录。
const getFiles = (dir, base = '') => readdirSync(dir, withFileTypes: true).reduce((files, file) =>
const filePath = path.join(dir, file.name)
const relativePath = path.join(base, file.name)
if(file.isDirectory())
return files.concat(getFiles(filePath, relativePath))
else if(file.isFile())
file.__fullPath = filePath
file.__relateivePath = relativePath
return files.concat(file)
, [])
【讨论】:
【参考方案14】:函数式编程
const fs = require('fs')
const path = require('path')
const R = require('ramda')
const getDirectories = pathName =>
const isDirectory = pathName => fs.lstatSync(pathName).isDirectory()
const mapDirectories = pathName => R.map(name => path.join(pathName, name), fs.readdirSync(pathName))
const filterDirectories = listPaths => R.filter(isDirectory, listPaths)
return
paths:R.pipe(mapDirectories)(pathName),
pathsFiltered: R.pipe(mapDirectories, filterDirectories)(pathName)
【讨论】:
【参考方案15】:此答案不使用 readdirSync
或 statSync
等阻塞函数。它不使用外部依赖,也不陷入回调地狱的深处。
相反,我们使用现代 JavaScript 便利,例如 Promises 和 async-await
语法。并且异步结果是并行处理的;不是顺序的-
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)
)
: []
我将安装一个示例包,然后测试我们的功能 -
$ npm install ramda
$ node
让我们看看它是否有效 -
> dirs (".") .then (console.log, console.error)
[ '.'
, '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'
]
使用广义模块Parallel
,我们可以简化dirs
的定义-
const Parallel =
require ("./Parallel")
const dirs = async (path = ".") =>
(await stat (path)) .isDirectory ()
? Parallel (readdir (path))
.flatMap (f => dirs (join (path, f)))
.then (results => [ path, ...results ])
: []
上面使用的Parallel
模块是从一组旨在解决类似问题的函数中提取的模式。更多解释请看related Q&A。
【讨论】:
【参考方案16】: var getDirectories = (rootdir , cb) =>
fs.readdir(rootdir, (err, files) =>
if(err) throw err ;
var dirs = files.map(filename => path.join(rootdir,filename)).filter( pathname => fs.statSync(pathname).isDirectory());
return cb(dirs);
)
getDirectories( myDirectories => console.log(myDirectories));``
【讨论】:
【参考方案17】:使用 ES6 的完全异步版本,只有原生包 fs.promises 和 async/await 并行执行文件操作:
const fs = require('fs');
const path = require('path');
async function listDirectories(rootPath)
const fileNames = await fs.promises.readdir(rootPath);
const filePaths = fileNames.map(fileName => path.join(rootPath, fileName));
const filePathsAndIsDirectoryFlagsPromises = filePaths.map(async filePath => (path: filePath, isDirectory: (await fs.promises.stat(filePath)).isDirectory()))
const filePathsAndIsDirectoryFlags = await Promise.all(filePathsAndIsDirectoryFlagsPromises);
return filePathsAndIsDirectoryFlags.filter(filePathAndIsDirectoryFlag => filePathAndIsDirectoryFlag.isDirectory)
.map(filePathAndIsDirectoryFlag => filePathAndIsDirectoryFlag.path);
经过测试,效果很好。
【讨论】:
【参考方案18】:?graph-fs
安装
npm i graph-fs
使用
const Node = require("graph-fs");
const directory = new Node("/path/to/directory");
const names = directory.children // <--
.filter(node => node.is.directory)
.map(directory => directory.name);
【讨论】:
以上是关于获取目录nodejs中的所有目录的主要内容,如果未能解决你的问题,请参考以下文章
如何使用nodejs获取节点docker容器中的主机主目录路径?
异步和递归目录扫描,用于 Nodejs 和 Expressjs 中的文件列表