如何在Node.js中递归读取目录

本文概述

  • A.使用自定义代码段
  • B.使用readdirp模块
  • C.使用klaw和klaw-sync模块
为了创建某种文件搜索算法或获取目录中所有文件和文件夹的列表以使用zlib对其进行压缩, 经常会搜索Node.js开发人员的此功能(以及某些组件上的下载和相关软件包的数量)已知模块证明了这一点)。
在本文中, 我们将向你展示如何递归循环目录, 以使用3种不同方法(自定义代码段或使用开放源代码模块)列出其所有内容。
A.使用自定义代码段【如何在Node.js中递归读取目录】如果要递归地遍历Node.js中的目录, 则不需要模块来实现它, 因为可以使用非常简单的递归函数。以下filewalker函数将为你解决问题。它期望作为第一个参数的字符串具有将要递归浏览的文件夹的路径, 而作为第二个参数的期望是当Providen路径中没有更多目录时执行的函数(回调)。回调接收2个参数, 第一个为错误, 结果数组包含文件和子目录的所有混合路径。
注意如果你只需要忽略所有文件夹并仅检索文件的路径, 则可以轻松地注释掉将文件路径存储在代码段内的行中。
const fs = require('fs'); const path = require('path'); /** * Explores recursively a directory and returns all the filepaths and folderpaths in the callback. * * @see http://stackoverflow.com/a/5827895/4241030 * @param {String} dir * @param {Function} done */function filewalker(dir, done) {let 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 directory, execute a recursive callif (stat & & stat.isDirectory()) {// Add directory to array [comment if you need to remove the directories from the array]results.push(file); filewalker(file, function(err, res){results = results.concat(res); if (!--pending) done(null, results); }); } else {results.push(file); if (!--pending) done(null, results); }}); }); }); };

它的用法非常简单:
filewalker("./some-existent-path", function(err, data){if(err){throw err; }// ["c://some-existent-path/file.txt", "c:/some-existent-path/subfolder"]console.log(data); });

如果你不想依靠模块直接在代码中实现非常简单的功能, 则此解决方案是完美的。
B.使用readdirp模块如果你的代码不是那么简单, 那么由于代码的复杂性, 仅提供一个摘要的解决方案可能对你来说不够。在这种情况下, 你可以使用readdirp模块, 是的, readdirp不是readdir。 readdirp是一个非常有用的模块, 它公开了Node.js文件系统模块中可用的readdir函数的递归版本, 此外还公开了流api。
要在你的项目中安装此模块, 请在终端中执行以下命令:
npm install readdirp

使用此模块的优势非常明显, 它不像我们在本文中介绍的第一个解决方案那么简单。除了允许你设置深度(在Providen目录中要探索的子文件夹的最大值)之外, 该模块还允许你通过文件扩展名和目录名称来过滤递归探索。它以以下方式工作, 你需要使用readdirp模块, 该模块基本上是一个函数。使用此功能, 你将能够递归遍历文件夹路径, 它需要一个指定设置的对象, 你可以在此处查看readdirp的所有可用选项:
var settings = {root: './a-folder-to-explore', entryType: 'all', // Filter files with js and json extensionfileFilter: [ '*.js', '*.json' ], // Filter by directorydirectoryFilter: [ '!.git', '!*modules' ], // Work with files up to 1 subdirectory deepdepth: 1};

基本上, 你只需要提供表明将浏览哪个目录的root属性。
该模块提供了两种使用方式, 第一种是与回调一起使用:
// Import the modulevar readdirp = require('readdirp'); var settings = {root: './your-folder-path', entryType: 'all'}; // In this example, this variable will store all the paths of the files and directories inside the providen pathvar allFilePaths = []; // Iterate recursively through a folderreaddirp(settings, // This callback is executed everytime a file or directory is found inside the providen pathfunction(fileInfo) {// Store the fullPath of the file/directory in our custom array allFilePaths.push(fileInfo.fullPath); }, // This callback is executed once function (err, res) {if(err){throw err; }// An array with all the fileEntry objects of the folder // console.log(res); console.log(allFilePaths); // ["c:/file.txt", ""]});

或通过流API:
// Import the modulevar readdirp = require('readdirp'); var settings = {root: './', entryType: 'all'}; // In this example, this variable will store all the paths of the files and directories inside the providen pathvar allFilePaths = []; // Iterate recursively through a folderreaddirp(settings).on('data', function (entry) {// execute everytime a file is found in the providen directory// Store the fullPath of the file/directory in our custom array allFilePaths.push(entry.fullPath); }).on('warn', function(warn){console.log("Warn: ", warn); }).on('error', function(err){console.log("Error: ", err); }).on('end', function(){console.log(allFilePaths); // ["c:/file.txt", "c:/other-file.txt" ...]});

每个fileEntry对象都具有以下结构, 因此, 你不仅将获得完整路径, 而且还将获得有关文件或目录的更多有用信息:
{name: 'index.js', path: 'node_modules\\string_decoder\\index.js', fullPath: 'C:\\Users\\sdkca\\Desktop\\node-workspace\\node_modules\\string_decoder\\index.js', parentDir: 'node_modules\\string_decoder', fullParentDir: 'C:\\Users\\sdkca\\Desktop\\node-workspace\\node_modules\\string_decoder', stat:Stats {dev: -469691281, mode: 33206, nlink: 1, uid: 0, gid: 0, rdev: 0, blksize: undefined, ino: 562949954035272, size: 7796, blocks: undefined, atime: 2017 - 03 - 31T18: 27:30.703Z, mtime: 2017 - 03 - 31T18: 27:30.724Z, ctime: 2017 - 03 - 31T18: 27:30.724Z, birthtime: 2017 - 03 - 31T18: 27:30.703Z }};

有关更多信息, 请在此处访问Github中的模块存储库。
C.使用klaw和klaw-sync模块最初, 很多开发人员过去都依赖于扳手模块(很多人仍然依赖), 但是现在正式不赞成使用它, 并且我们喜欢推广标准, 你不应该再使用它(我们不禁止使用它, 因此随时可以如果你愿意, 请探索该模块, 但请考虑一下是否已弃用)。该项目现在建议使用fs-extra模块, 但是该模块不再支持walk()和walkSync()函数(这是开发人员为什么使用扳手模块递归浏览目录的原因)。
由于递归功能不再可用, 因此fs-extra模块建议使用klaw模块。此模块使用最初从fs-extra模块提取的Readable流接口公开异步Node.js文件系统walker。
要安装此模块, 请在终端中执行以下命令:
npm install klaw

Klaw非常易于使用和可定制。要递归遍历目录, 请使用以下代码段:
// Import the klaw modulevar klaw = require('klaw'); // an array to store the folder and files insidevar items = []; var directoryToExplore = "./some-folder"; klaw(directoryToExplore).on('data', function (item) {items.push(item.path)}).on('end', function () {console.log(items); }).on('error', function (err, item) {console.log(err.message)console.log(item.path) // the file the error occurred on});

由于它是异步的, 因此你需要依靠end回调对Providen目录中找到的文件和目录列表进行任何操作。
有关异步klaw模块的更多信息, 请访问Github上的官方存储库。
如果你需要相同的功能但需要同步, 则可以使用klaw-sync模块。 klaw-sync是Node.js递归文件系统walker, 它是klaw的同步副本。它递归列出目录中的所有文件和目录, 并返回一个对象数组, 每个对象都有两个属性:path和stats。 path是文件或目录的完整路径, 而stats是fs.Stats的实例。
要安装此模块, 请在终端中执行以下命令:
npm install klaw-sync

klaw的同步版本与异步版本一样易于使用, 但是比其对应版本更具可定制性。你可以使用以下代码段浏览目录:
// Require the modulevar klawSync = require('klaw-sync'); // Create an empty variable to be accesible in the closurevar paths; // The directory that you want to explorevar directoryToExplore = "./folder-to-explore"; try {paths = klawSync(directoryToExplore); } catch (err) {console.error(err); }// [//{path:"c:/file.txt", stats: {..File information..}}, //{path:"c:/file.txt", stats: {..File information..}}, //{path:"c:/file.txt", stats: {..File information..}}, // ]console.log(paths);

klaw-sync允许你按扩展名(名称)过滤目录和文件。此外, 你可以通过设置以下选项来仅搜索目录或文件:
var klawSync = require('klaw-sync'); var directoryToExplore = "./some-folder"; var files = klawSync(directoryToExplore, {nodir: true}); // [//{path:"c:/file.txt", stats: {..File information..}}, //{path:"c:/file2.txt", stats: {..File information..}}, //{path:"c:/file3.txt", stats: {..File information..}}, // ]console.log(files); var paths = klawSync(directoryToExplore, {nofile: true}); // [//{path:"c:/folder", stats: {..Folder information..}}, //{path:"c:/folder2", stats: {..Folder information..}}, //{path:"c:/folder3", stats: {..Folder information..}}, // ]console.log(paths);

有关同步爪模块的更多信息, 请访问Github上的官方存储库。
Klaw代表走动(但向后走), 事实证明(截至2017年1月25日), 在大多数情况下, klaw和klaw-sync比其他模块快。
编码愉快!

    推荐阅读