【问题标题】:Get all directories within directory nodejs获取目录nodejs中的所有目录
【发布时间】:2023-03-31 12:38:01
【问题描述】:

我希望这会是一件简单的事情,但我找不到任何可以这样做的东西。

我只想获取给定文件夹/目录中的所有文件夹/目录。

例如:

<MyFolder>
|- SomeFolder
|- SomeOtherFolder
|- SomeFile.txt
|- SomeOtherFile.txt
|- x-directory

我希望得到一个数组:

["SomeFolder", "SomeOtherFolder", "x-directory"]

或者上面的路径,如果这是它的服务方式......

那么,是否已经存在可以执行上述操作的东西了?

【问题讨论】:

    标签: node.js 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 =&gt; fs.readdirSync(srcPath).filter(file =&gt; 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 版本。你可以有这样的东西。

              1. 记录目录长度,用它作为指标来判断所有异步统计任务是否完成。

              2. 如果异步统计任务完成,则所有文件统计都已检查,所以调用回调

              这仅在 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 选项设置为 truefs.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 =&gt; c.isDirectory()) 会比使用reduce() 更简单
              • 是的,但是 filter 返回一个 fs.Dirent 对象数组,它们是目录。 OP 想要目录名称。
              • 是的,我还是更喜欢.filter(c =&gt; c.isDirectory()).map(c =&gt; 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】:

                  此答案不使用 readdirSyncstatSync 等阻塞函数。它不使用外部依赖,也不陷入回调地狱的深处。

                  相反,我们使用现代 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);
                        

                        【讨论】:

                          猜你喜欢
                          • 2020-01-31
                          • 2017-05-18
                          • 1970-01-01
                          • 2011-02-01
                          • 2010-11-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2021-09-18
                          相关资源
                          最近更新 更多