在Node.JS中遍历文件夹中的文件

126

我正在尝试循环遍历目录中的文件,但是在实现时遇到了一些问题。如何将多个文件导入并移动到另一个文件夹中?

var dirname = 'C:/FolderwithFiles';
console.log("Going to get file info!");
fs.stat(dirname, function (err, stats) {
    if (err) {
        return console.error(err);
    }
    console.log(stats);
    console.log("Got file info successfully!");

    // Check file type
    console.log("isFile ? " + stats.isFile());
    console.log("isDirectory ? " + stats.isDirectory());
});

7
谢谢,但是我知道。我看了API,但遇到了麻烦,所以来这里寻求帮助。 - Prolasis
4个回答

157

使用回调函数的旧答案

您想使用fs.readdir函数获取目录内容,使用fs.rename函数进行重命名。如果需要在运行后续代码之前等待它们完成,则这两个函数都有同步版本。

我编写了一个快速脚本来执行您所描述的操作。

var fs = require('fs');
var path = require('path');
// In newer Node.js versions where process is already global this isn't necessary.
var process = require("process");

var moveFrom = "/home/mike/dev/node/sonar/moveme";
var moveTo = "/home/mike/dev/node/sonar/tome"

// Loop through all the files in the temp directory
fs.readdir(moveFrom, function (err, files) {
  if (err) {
    console.error("Could not list the directory.", err);
    process.exit(1);
  }

  files.forEach(function (file, index) {
    // Make one pass and make the file complete
    var fromPath = path.join(moveFrom, file);
    var toPath = path.join(moveTo, file);

    fs.stat(fromPath, function (error, stat) {
      if (error) {
        console.error("Error stating file.", error);
        return;
      }

      if (stat.isFile())
        console.log("'%s' is a file.", fromPath);
      else if (stat.isDirectory())
        console.log("'%s' is a directory.", fromPath);

      fs.rename(fromPath, toPath, function (error) {
        if (error) {
          console.error("File moving error.", error);
        } else {
          console.log("Moved file '%s' to '%s'.", fromPath, toPath);
        }
      });
    });
  });
});

我在本地机器上进行了测试。

node testme.js 
'/home/mike/dev/node/sonar/moveme/hello' is a file.
'/home/mike/dev/node/sonar/moveme/test' is a directory.
'/home/mike/dev/node/sonar/moveme/test2' is a directory.
'/home/mike/dev/node/sonar/moveme/test23' is a directory.
'/home/mike/dev/node/sonar/moveme/test234' is a directory.
Moved file '/home/mike/dev/node/sonar/moveme/hello' to '/home/mike/dev/node/sonar/tome/hello'.
Moved file '/home/mike/dev/node/sonar/moveme/test' to '/home/mike/dev/node/sonar/tome/test'.
Moved file '/home/mike/dev/node/sonar/moveme/test2' to '/home/mike/dev/node/sonar/tome/test2'.
Moved file '/home/mike/dev/node/sonar/moveme/test23' to '/home/mike/dev/node/sonar/tome/test23'.
Moved file '/home/mike/dev/node/sonar/moveme/test234' to '/home/mike/dev/node/sonar/tome/test234'.

更新:使用async/await的fs.promises函数

受ma11hew28答案的启发(在此处显示),这里与上面相同,但使用fs.promises中的异步函数。正如ma11hew28所指出的那样,这可能存在内存限制,相对于在v12.12.0中添加的fs.promises.opendir

以下是快速代码。

//jshint esversion:8
//jshint node:true
const fs = require( 'fs' );
const path = require( 'path' );

const moveFrom = "/tmp/movefrom";
const moveTo = "/tmp/moveto";

// Make an async function that gets executed immediately
(async ()=>{
    // Our starting point
    try {
        // Get the files as an array
        const files = await fs.promises.readdir( moveFrom );

        // Loop them all with the new for...of
        for( const file of files ) {
            // Get the full paths
            const fromPath = path.join( moveFrom, file );
            const toPath = path.join( moveTo, file );

            // Stat the file to see if we have a file or dir
            const stat = await fs.promises.stat( fromPath );

            if( stat.isFile() )
                console.log( "'%s' is a file.", fromPath );
            else if( stat.isDirectory() )
                console.log( "'%s' is a directory.", fromPath );

            // Now move async
            await fs.promises.rename( fromPath, toPath );

            // Log because we're crazy
            console.log( "Moved '%s'->'%s'", fromPath, toPath );
        } // End for...of
    }
    catch( e ) {
        // Catch anything bad that happens
        console.error( "We've thrown! Whoops!", e );
    }

})(); // Wrap in parenthesis and call now

为了完整起见,以下行在之前存在:var fs = require('fs'); var path = require('path'); - Craig Myles
1
process 是一个全局对象,你不需要 require 它。否则很好,谢谢! - kjonsson
1
以前的版本是这样做的。它以前需要被 "global.process" 引用,而所需的文件则将其公开。感谢评论。 - Mikey A. Leonetti
如果您需要使用通配符,请参见https://dev59.com/vWEi5IYBdhLWcg3wZ7ga#21320251。 - TrueWill
我对这个解决方案唯一的担忧是,无法控制应用程序的流程。如果这对您的应用程序很重要-您可以考虑使用promise链。 - Kevin Friedheim

44

fs.readdir(path[, options], callback)(Mikey A. Leonetti 在 他的回答中使用)以及它的变种(fsPromises.readdir(path[, options])fs.readdirSync(path[, options]))会一次性将目录的所有条目都读入内存中。这对于大多数情况很好,但如果目录中有非常多的条目和/或您想要降低应用程序的内存占用,则可以改为逐个迭代目录的条目。

异步方式

目录是异步可迭代的,因此你可以像这样做:

const fs = require('fs')

async function ls(path) {
  const dir = await fs.promises.opendir(path)
  for await (const dirent of dir) {
    console.log(dirent.name)
  }
}

ls('.').catch(console.error)

或者,您可以直接使用dir.read()和/或dir.read(callback)

同步方式

目录不能同步遍历,但是您可以直接使用dir.readSync()。例如:

const fs = require('fs')

const dir = fs.opendirSync('.')
let dirent
while ((dirent = dir.readSync()) !== null) {
  console.log(dirent.name)
}
dir.closeSync()

或者,您可以使目录同步可迭代。例如:

const fs = require('fs')

function makeDirectoriesSyncIterable() {
  const p = fs.Dir.prototype
  if (p.hasOwnProperty(Symbol.iterator)) { return }
  const entriesSync = function* () {
    try {
      let dirent
      while ((dirent = this.readSync()) !== null) { yield dirent }
    } finally { this.closeSync() }
  }
  if (!p.hasOwnProperty(entriesSync)) { p.entriesSync = entriesSync }
  Object.defineProperty(p, Symbol.iterator, {
    configurable: true,
    enumerable: false,
    value: entriesSync,
    writable: true
  })
}
makeDirectoriesSyncIterable()

然后,您可以像这样做:

const dir = fs.opendirSync('.')
for (const dirent of dir) {
  console.log(dirent.name)
}
注意:“在繁忙的进程中,请使用这些调用的异步版本。同步版本将阻止整个进程,直到它们完成,停止所有连接。”
参考资料:

1
fs.promises... - 不错的一个 - sonnenhaft
值得一提的是,for await 需要 Node 10.0.0+ 或者 8.10.0+ 并且使用 --harmony-async-iteration。 - Rondo
3
请注意,fs.opendir及其变体是在v12.12.0版本中引入的。 https://dev59.com/yrnoa4cB1Zd3GeqPNkiJ - edmundo096
1
如果有人开始想知道是否需要手动await dir.close(),答案是否定的:https://dev59.com/AL3pa4cB1Zd3GeqPZTGO#63840108 - Nick

9

读取目录中的所有文件夹

  const readAllFolder = (dirMain) => {
  const readDirMain = fs.readdirSync(dirMain);

  console.log(dirMain);
  console.log(readDirMain);

  readDirMain.forEach((dirNext) => {
    console.log(dirNext, fs.lstatSync(dirMain + "/" + dirNext).isDirectory());
    if (fs.lstatSync(dirMain + "/" + dirNext).isDirectory()) {
      readAllFolder(dirMain + "/" + dirNext);
    } 
  });
};

1

所提供的答案仅适用于单个文件夹。这里是一个异步实现,适用于多个文件夹,其中所有文件夹同时处理,但较小的文件夹或文件会先完成。

如果您有任何反馈,请评论

异步多文件夹

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

// Multiple folders list
const in_dir_list = [
  'Folder 1 Large',
  'Folder 2 Small', // small folder and files will complete first
  'Folder 3 Extra Large'
]

// BEST PRACTICES: (1) Faster folder list For loop has to be outside async_capture_callback functions for async to make sense
//                 (2) Slower Read Write or I/O processes best be contained in an async_capture_callback functions because these processes are slower than for loop events and faster completed items get callback-ed out first 

for (i = 0; i < in_dir_list.length; i++) {
  var in_dir = in_dir_list[i]

  // function is created (see below) so each folder is processed asynchronously for readFile_async that follows
  readdir_async_capture(in_dir, function(files_path) {
    console.log("Processing folders asynchronously ...")

    for (j = 0; j < files_path.length; j++) {
      file_path = files_path[j]
      file = file_path.substr(file_path.lastIndexOf("/") + 1, file_path.length)

      // function is created (see below) so all files are read simultaneously but the smallest file will be completed first and get callback-ed first 
      readFile_async_capture(file_path, file, function(file_string) {
        try {
          console.log(file_path)
          console.log(file_string)
        } catch (error) {
          console.log(error)
          console.log("System exiting first to catch error if not async will continue...")
          process.exit()
        }
      })
    }
  })
}

// fs.readdir async_capture function to deal with asynchronous code above
function readdir_async_capture(in_dir, callback) {
  fs.readdir(in_dir, function(error, files) {
    if (error) { return console.log(error) }
    files_path = files.map(function(x) { return path.join(in_dir, x) })
    callback(files_path)
  })
}

// fs.readFile async_capture function to deal with asynchronous code above
function readFile_async_capture(file_path, file, callback) {
  fs.readFile(file_path, function(error, data) {
    if (error) { return console.log(error) }
    file_string = data.toString()
    callback(file_string)
  })
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接