I\'m trying to get a list of the names of all the files present in a directory using Node.js. I want output that is an array of filenames. How can I do this?
问题:
回答1:
You can use the fs.readdir
or fs.readdirSync
methods.
fs.readdir
const testFolder = \'./tests/\';
const fs = require(\'fs\');
fs.readdir(testFolder, (err, files) => {
files.forEach(file => {
console.log(file);
});
})
fs.readdirSync
const testFolder = \'./tests/\';
const fs = require(\'fs\');
fs.readdirSync(testFolder).forEach(file => {
console.log(file);
})
The difference between the two methods, is that the first one is asynchronous, so you have to provide a callback function that will be executed when the read process ends.
The second is synchronous, it will return the file name array, but it will stop any further execution of your code until the read process ends.
回答2:
The answer above does not perform a recursive search into the directory though. Here\'s what I did for a recursive search (using node-walk: npm install walk
)
var walk = require(\'walk\');
var files = [];
// Walker options
var walker = walk.walk(\'./test\', { followLinks: false });
walker.on(\'file\', function(root, stat, next) {
// Add this file to the list of files
files.push(root + \'/\' + stat.name);
next();
});
walker.on(\'end\', function() {
console.log(files);
});
回答3:
IMO the most convinient way to do such tasks is to use a glob tool. Here\'s a glob package for node.js. Install with
npm install glob
Then use wild card to match filenames (example taken from package\'s website)
var glob = require(\"glob\")
// options is optional
glob(\"**/*.js\", options, function (er, files) {
// files is an array of filenames.
// If the `nonull` option is set, and nothing
// was found, then files is [\"**/*.js\"]
// er is an error object or null.
})
回答4:
Get files in all subdirs
function getFiles (dir, files_){
files_ = files_ || [];
var files = fs.readdirSync(dir);
for (var i in files){
var name = dir + \'/\' + files[i];
if (fs.statSync(name).isDirectory()){
getFiles(name, files_);
} else {
files_.push(name);
}
}
return files_;
}
console.log(getFiles(\'path/to/dir\'))
回答5:
Here\'s a simple solution using only the native fs
and path
modules:
// sync version
function walkSync(currentDirPath, callback) {
var fs = require(\'fs\'),
path = require(\'path\');
fs.readdirSync(currentDirPath).forEach(function (name) {
var filePath = path.join(currentDirPath, name);
var stat = fs.statSync(filePath);
if (stat.isFile()) {
callback(filePath, stat);
} else if (stat.isDirectory()) {
walkSync(filePath, callback);
}
});
}
or async version (uses fs.readdir
instead):
// async version with basic error handling
function walk(currentDirPath, callback) {
var fs = require(\'fs\'),
path = require(\'path\');
fs.readdir(currentDirPath, function (err, files) {
if (err) {
throw new Error(err);
}
files.forEach(function (name) {
var filePath = path.join(currentDirPath, name);
var stat = fs.statSync(filePath);
if (stat.isFile()) {
callback(filePath, stat);
} else if (stat.isDirectory()) {
walk(filePath, callback);
}
});
});
}
Then you just call (for sync version):
walkSync(\'path/to/root/dir\', function(filePath, stat) {
// do something with \"filePath\"...
});
or async version:
walk(\'path/to/root/dir\', function(filePath, stat) {
// do something with \"filePath\"...
});
The difference is in how node blocks while performing the IO. Given that the API above is the same, you could just use the async version to ensure maximum performance.
However there is one advantage to using the synchronous version. It is easier to execute some code as soon as the walk is done, as in the next statement after the walk. With the async version, you would need some extra way of knowing when you are done. Perhaps creating a map of all paths first, then enumerating them. For simple build/util scripts (vs high performance web servers) you could use the sync version without causing any damage.
回答6:
Using Promises with ES7
Asynchronous use with mz/fs
The mz
module provides promisified versions of the core node library. Using them is simple. First install the library...
npm install mz
Then...
const fs = require(\'mz/fs\');
fs.readdir(\'./myDir\').then(listing => console.log(listing))
.catch(err => console.error(err));
Alternatively you can write them in asynchronous functions in ES7:
async function myReaddir () {
try {
const file = await fs.readdir(\'./myDir/\');
}
catch (err) { console.error( err ) }
};
Update for recursive listing
Some of the users have specified a desire to see a recursive listing (though not in the question)... Use fs-promise
. It\'s a thin wrapper around mz
.
npm install fs-promise;
then...
const fs = require(\'fs-promise\');
fs.walk(\'./myDir\').then(
listing => listing.forEach(file => console.log(file.path))
).catch(err => console.error(err));
回答7:
Dependencies.
var fs = require(\'fs\');
var path = require(\'path\');
Definition.
// String -> [String]
function fileList(dir) {
return fs.readdirSync(dir).reduce(function(list, file) {
var name = path.join(dir, file);
var isDir = fs.statSync(name).isDirectory();
return list.concat(isDir ? fileList(name) : [name]);
}, []);
}
Usage.
var DIR = \'/usr/local/bin\';
// 1. List all files in DIR
fileList(DIR);
// => [\'/usr/local/bin/babel\', \'/usr/local/bin/bower\', ...]
// 2. List all file names in DIR
fileList(DIR).map((file) => file.split(path.sep).slice(-1)[0]);
// => [\'babel\', \'bower\', ...]
Please note that fileList
is way too optimistic. For anything serious, add some error handling.
回答8:
You don\'t say you want to do it recursively so I assume you only need direct children of the directory.
Sample code:
const fs = require(\'fs\');
const path = require(\'path\');
fs.readdirSync(\'your-directory-path\')
.filter((file) => fs.lstatSync(path.join(folder, file)).isFile());
回答9:
Load fs
:
const fs = require(\'fs\');
Read files async:
fs.readdir(\'./dir\', function (err, files) {
// \"files\" is an Array with files names
});
Read files sync:
var files = fs.readdirSync(\'./dir\');
回答10:
Here\'s an asynchronous recursive version.
function ( path, callback){
// the callback gets ( err, files) where files is an array of file names
if( typeof callback !== \'function\' ) return
var
result = []
, files = [ path.replace( /\\/\\s*$/, \'\' ) ]
function traverseFiles (){
if( files.length ) {
var name = files.shift()
fs.stat(name, function( err, stats){
if( err ){
if( err.errno == 34 ) traverseFiles()
// in case there\'s broken symbolic links or a bad path
// skip file instead of sending error
else callback(err)
}
else if ( stats.isDirectory() ) fs.readdir( name, function( err, files2 ){
if( err ) callback(err)
else {
files = files2
.map( function( file ){ return name + \'/\' + file } )
.concat( files )
traverseFiles()
}
})
else{
result.push(name)
traverseFiles()
}
})
}
else callback( null, result )
}
traverseFiles()
}
回答11:
Took the general approach of @Hunan-Rostomyan, made it a litle more concise and added excludeDirs
argument. It\'d be trivial to extend with includeDirs
, just follow same pattern:
import * as fs from \'fs\';
import * as path from \'path\';
function fileList(dir, excludeDirs?) {
return fs.readdirSync(dir).reduce(function (list, file) {
const name = path.join(dir, file);
if (fs.statSync(name).isDirectory()) {
if (excludeDirs && excludeDirs.length) {
excludeDirs = excludeDirs.map(d => path.normalize(d));
const idx = name.indexOf(path.sep);
const directory = name.slice(0, idx === -1 ? name.length : idx);
if (excludeDirs.indexOf(directory) !== -1)
return list;
}
return list.concat(fileList(name, excludeDirs));
}
return list.concat([name]);
}, []);
}
Example usage:
console.log(fileList(\'.\', [\'node_modules\', \'typings\', \'bower_components\']));
回答12:
Get sorted
filenames. You can filter results based on a specific extension
such as \'.txt\'
, \'.jpg\'
and so on.
import * as fs from \'fs\';
import * as Path from \'path\';
function getFilenames(path, extension) {
return fs
.readdirSync(path)
.filter(
item =>
fs.statSync(Path.join(path, item)).isFile() &&
(extension === undefined || Path.extname(item) === extension)
)
.sort();
}
回答13:
Just a heads up: if you\'re planning to perform operations on each file in a directory, try vinyl-fs (which is used by gulp, the streaming build system).
回答14:
I made a node module to automate this task: mddir
Usage
node mddir \"../relative/path/\"
To install: npm install mddir -g
To generate markdown for current directory: mddir
To generate for any absolute path: mddir /absolute/path
To generate for a relative path: mddir ~/Documents/whatever.
The md file gets generated in your working directory.
Currently ignores node_modules, and .git folders.
Troubleshooting
If you receive the error \'node\\r: No such file or directory\', the issue is that your operating system uses different line endings and mddir can\'t parse them without you explicitly setting the line ending style to Unix. This usually affects Windows, but also some versions of Linux. Setting line endings to Unix style has to be performed within the mddir npm global bin folder.
Line endings fix
Get npm bin folder path with:
npm config get prefix
Cd into that folder
brew install dos2unix
dos2unix lib/node_modules/mddir/src/mddir.js
This converts line endings to Unix instead of Dos
Then run as normal with: node mddir \"../relative/path/\".
Example generated markdown file structure \'directoryList.md\'
|-- .bowerrc
|-- .jshintrc
|-- .jshintrc2
|-- Gruntfile.js
|-- README.md
|-- bower.json
|-- karma.conf.js
|-- package.json
|-- app
|-- app.js
|-- db.js
|-- directoryList.md
|-- index.html
|-- mddir.js
|-- routing.js
|-- server.js
|-- _api
|-- api.groups.js
|-- api.posts.js
|-- api.users.js
|-- api.widgets.js
|-- _components
|-- directives
|-- directives.module.js
|-- vendor
|-- directive.draganddrop.js
|-- helpers
|-- helpers.module.js
|-- proprietary
|-- factory.actionDispatcher.js
|-- services
|-- services.cardTemplates.js
|-- services.cards.js
|-- services.groups.js
|-- services.posts.js
|-- services.users.js
|-- services.widgets.js
|-- _mocks
|-- mocks.groups.js
|-- mocks.posts.js
|-- mocks.users.js
|-- mocks.widgets.js
回答15:
Use npm
list-contents module. It reads the contents and sub-contents of the given directory and returns the list of files\' and folders\' paths.
const list = require(\'list-contents\');
list(\"./dist\",(o)=>{
if(o.error) throw o.error;
console.log(\'Folders: \', o.dirs);
console.log(\'Files: \', o.files);
});
回答16:
function getFilesRecursiveSync(dir, fileList, optionalFilterFunction) {
if (!fileList) {
grunt.log.error(\"Variable \'fileList\' is undefined or NULL.\");
return;
}
var files = fs.readdirSync(dir);
for (var i in files) {
if (!files.hasOwnProperty(i)) continue;
var name = dir + \'/\' + files[i];
if (fs.statSync(name).isDirectory()) {
getFilesRecursiveSync(name, fileList, optionalFilterFunction);
} else {
if (optionalFilterFunction && optionalFilterFunction(name) !== true)
continue;
fileList.push(name);
}
}
}