Как получить список имен всех файлов, присутствующих в каталоге в Node.js?

716

Я пытаюсь получить список имен всех файлов, присутствующих в каталоге, используя Node.js. Мне нужен вывод, который представляет собой массив имен файлов. Как я могу это сделать?

  • 7
    fs.readdir работает, но не может использовать шаблоны имен файлов, такие как ls /tmp/*core* . Проверьте github.com/isaacs/node-glob . Глобусы могут даже искать в подкаталогах.
  • 0
    readdir-recursive модуль readdir-recursive хотя, если вы ищете имена файлов в подкаталогах, также
Показать ещё 1 комментарий
Теги:

18 ответов

966
Лучший ответ

Вы можете использовать методы fs.readdir или fs.readdirSync.

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);
})

Разница между этими двумя методами заключается в том, что первый метод является асинхронным, поэтому вы должны предоставить функцию обратного вызова, которая будет выполняться после завершения процесса чтения.

Второй является синхронным, он вернет массив имен файлов, но остановит дальнейшее выполнение вашего кода, пока не завершится процесс чтения.

  • 170
    Примечание: readdir также показывает имена каталогов . Чтобы отфильтровать их, используйте fs.stat(path, callback(err, stats)) и stats.isDirectory() .
  • 2
    Я должен добавить, что, скорее всего, вам следует использовать readdire, потому что вы не хотите блокировать IO в узле.
Показать ещё 7 комментариев
172

IMO самым удобным способом выполнения таких задач является использование инструмента glob. Здесь glob-пакет для node.js. Установите с помощью

npm install glob

Затем используйте wild card для соответствия именам файлов (пример, взятый с веб-сайта пакета)

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
    это было лучшее решение для меня, так как я хотел указать тип файла проще, чем сравнение строк. Благодарю.
  • 1
    Самое элегантное решение на мой взгляд, потому что оно простое и привычное. Спасибо
Показать ещё 4 комментария
169

В приведенном выше ответе не выполняется рекурсивный поиск в каталоге. Вот что я сделал для рекурсивного поиска (используя 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);
});
  • 2
    fs.readdirSync лучше, нативная альтернатива, созданная специально для этого.
  • 34
    К сожалению, fs.readdirSync не входит в подкаталоги, если только вы не захотите написать свою собственную подпрограмму, чтобы сделать именно это, чего вы не сделали, учитывая, что уже существуют модули npm для решения этой самой проблемы.
Показать ещё 6 комментариев
85

Получить файлы во всех поддиректорах

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'))
  • 7
    Он явно возвращает список файлов.
  • 3
    Почему, if (typeof files_ === 'undefined') files_=[]; ? вам нужно только сделать var files_ = files_ || []; вместо files_ = files_ || []; ,
Показать ещё 1 комментарий
55

Здесь простое решение, использующее только собственные fs и path модули:

// 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);
        }
    });
}

или асинхронная версия (вместо этого используется fs.readdir):

// 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);
            }
        });
    });
}

Затем вы просто вызываете (для версии синхронизации):

walkSync('path/to/root/dir', function(filePath, stat) {
    // do something with "filePath"...
});

или асинхронная версия:

walk('path/to/root/dir', function(filePath, stat) {
    // do something with "filePath"...
});

Разница заключается в том, как блокировать node при выполнении IO. Учитывая, что вышеприведенный API один и тот же, вы можете просто использовать версию async для обеспечения максимальной производительности.

Однако есть одно преимущество использования синхронной версии. Легче выполнить какой-то код, как только будет выполнена прогулка, как в следующем заявлении после прогулки. С асинхронной версией вам понадобится дополнительный способ узнать, когда вы закончите. Возможно сначала создать карту всех путей, а затем перечислить их. Для простых скриптов build/util (vs высокопроизводительных веб-серверов) вы можете использовать версию синхронизации без каких-либо повреждений.

  • 0
    Должно быть хорошо сейчас, верно?
  • 1
    Должен заменить строку в walkSync из walk(filePath, callback); to walkSync(filePath, callback);
Показать ещё 3 комментария
22

Использование Promises с ES7

Асинхронное использование с mz/fs

Модуль mz предоставляет многообещающие версии основной библиотеки node. Использование их просто. Сначала установите библиотеку...

npm install mz

Тогда...

const fs = require('mz/fs');
fs.readdir('./myDir').then(listing => console.log(listing))
  .catch(err => console.error(err));

В качестве альтернативы вы можете записать их в асинхронных функциях в ES7:

async function myReaddir () {
  try {
    const file = await fs.readdir('./myDir/');
  }
  catch (err) { console.error( err ) }
};

Обновление для рекурсивного перечисления

Некоторые пользователи указали на желание увидеть рекурсивный список (хотя и не в вопросе)... Используйте fs-promise. Это тонкая обертка вокруг mz.

npm install fs-promise;

то...

const fs = require('fs-promise');
fs.walk('./myDir').then(
    listing => listing.forEach(file => console.log(file.path))
).catch(err => console.error(err));
15

Зависимости.

var fs = require('fs');
var path = require('path');

Определение.

// 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', ...]

Обратите внимание, что fileList слишком оптимистичен. Для чего-нибудь серьезного, добавьте некоторые ошибки обработки.

  • 0
    Я также добавил excludeDirs массива excludeDirs . Это меняет его настолько, что, возможно, вы должны отредактировать его (если хотите). В противном случае я добавлю это в другом ответе. gist.github.com/AlecTaylor/f3f221b4fb86b4375650
  • 1
    @AT Отлично! Вы должны опубликовать свой собственный ответ, так как это полезное расширение. Давайте оставим этот безликим.
9

Вы не говорите, что хотите сделать это рекурсивно, поэтому я предполагаю, что вам нужны только прямые дети из каталога.

Пример кода:

const fs = require('fs');
const path = require('path');

fs.readdirSync('your-directory-path')
  .filter((file) => fs.lstatSync(path.join(folder, file)).isFile());
7

Загрузить fs:

const fs = require('fs');

Чтение файлов async:

fs.readdir('./dir', function (err, files) {
    // "files" is an Array with files names
});

Чтение файлов:

var files = fs.readdirSync('./dir');
4

Здесь представлена ​​асинхронная рекурсивная версия.

    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 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()
    }
  • 3
    Получите привычку добавлять точки с запятой в конце ваших утверждений. Вы не можете минимизировать код в противном случае. Тем не менее, спасибо за столь необходимый асинхронный вклад.
  • 0
    @ user2867288 Нет, вам не нужно добавлять точки с запятой. Проверьте стандарт .
Показать ещё 1 комментарий
3

Получить sorted имена файлов. Вы можете фильтровать результаты по определенному extension например, '.txt', '.jpg' и так далее.

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();
}
3

Взял общий подход @Хунань-Ростомян, сделал его более лаконичным и добавил аргумент excludeDirs. Было бы тривиально распространяться с помощью includeDirs, просто следуйте одному и тому же шаблону:

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]);
    }, []);
}

Пример использования:

console.log(fileList('.', ['node_modules', 'typings', 'bower_components']));
  • 0
    У меня есть основная папка: scss, а внутри нее другая папка: темы, но в итоговом списке отображаются все каталоги, а не только каталоги без каталога исключений, что происходит?
  • 0
    Работает только с '.' папка directory, с остальными каталогами не работает.
0

Начиная с Node v10.10.0, можно использовать новую опцию withFileTypes для fs.readdir и fs.readdirSync в сочетании с dirent.isDirectory() для фильтрации имен файлов в каталоге. Это выглядит так:

fs.readdirSync('./dirpath', {withFileTypes: true})
.filter(item => !item.isDirectory())
.map(item => item.name)

Возвращенный массив имеет вид:

['file1.txt', 'file2.txt', 'file3.txt']

Документы для класса fs.Dirent

0

если кто-то все еще ищет это, я делаю это:

import fs from 'fs';
import path from 'path';

const getAllFiles = dir =>
    fs.readdirSync(dir).reduce((files, file) => {
        const name = path.join(dir, file);
        const isDirectory = fs.statSync(name).isDirectory();
        return isDirectory ? [...files, ...getAllFiles(name)] : [...files, name];
    }, []);

и его работа очень хорошо для меня

0

Используйте модуль списка содержимого npm. Он читает содержимое и под-содержимое данного каталога и возвращает список путей к файлам и папкам.

const list = require('list-contents');

list("./dist",(o)=>{
  if(o.error) throw o.error;
   console.log('Folders: ', o.dirs);
   console.log('Files: ', o.files);
});
0

Я сделал модуль node для автоматизации этой задачи: mddir

Использование

node mddir "../relative/path/"

Для установки: npm install mddir -g

Чтобы создать уценку для текущего каталога: mddir

Сгенерировать для любого абсолютного пути: mddir/absolute/path

Сгенерировать для относительного пути: mddir ~/Documents/whatever.

Файл md создается в вашем рабочем каталоге.

В настоящее время игнорирует папки node_modules и .git.

Устранение неполадок

Если вы получили сообщение об ошибке "node\r: Нет такого файла или каталога", проблема заключается в том, что ваша операционная система использует разные концы строк, и mddir не может их разобрать без явной установки стиля окончания строки в Unix, Это обычно затрагивает Windows, но также некоторые версии Linux. Настройка конца строки в стиле Unix должна выполняться в глобальной папке bin mddir npm.

Исправления строк

Получить путь к папке npm bin с помощью:

npm config get prefix

Cd в эту папку

brew установить dos2unix

dos2unix lib/ node_modules/mddir/src/mddir.js

Это преобразует окончание строк в Unix вместо Dos

Затем выполните как обычно: node mddir "../relative/path/".

Пример сгенерированной файловой структуры markdown '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
0

Просто голова: если вы планируете выполнять операции над каждым файлом в каталоге, попробуйте vinyl-fs (который используется gulp, система потоковой сборки).

-1
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);
        }
    }
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню