Node.js本地文件操作之文件拷贝与目录遍历的方法

作者:慢半拍时间:2026-10-29 21:53:17浏览:265

本文介绍Node.js中文件拷贝与目录遍历的实现方法。小文件可直接读写,大文件需使用Stream管道传输以避免内存溢出。目录遍历采用深度优先+先序遍历算法,分别演示了同步与异步两种实现方式,并指出递归性能问题及优化方向。

Node.js文件拷贝实现

文件复制操作 Node.js 内置了基础的文件处理接口,但未涵盖如文件复制这类高级功能,故以此程序作为入门示例。该程序需兼容源文件路径与目标文件路径两个参数,类似于 copy 命令的行为。

小型文件复制 借助 Node.js 内置的 fs 模块,可简单构建此程序,实现方式如下。


var fs = require('fs');

function copy(src, dst) {
  fs.writeFileSync(dst, fs.readFileSync(src));
}

function main(argv) {
  copy(argv[0], argv[1]);
}

main(process.argv.slice(2));

上述代码通过 fs.readFileSync 从源路径读取文件数据,并借助 fs.writeFileSync 将数据写入目标路径。

技术细节: process 为全局对象,利用 process.argv 可获取命令行参数。其中 argv[0] 恒定为 Node.js 执行文件的绝对路径,argv[1] 恒定为主模块的绝对路径,故首个用户提供的命令行参数起始于 argv[2]。

大型文件复制 前述程序处理小文件时表现良好,但因其策略为一次性将全部文件内容载入内存后写入磁盘,故不适用于大文件复制,否则会导致内存溢出。针对大文件,应采取边读边写的策略直至复制完成,因此需对前述程序进行如下改造。


var fs = require('fs');

function copy(src, dst) {
  fs.createReadStream(src).pipe(fs.createWriteStream(dst));
}

function main(argv) {
  copy(argv[0], argv[1]);
}

main(process.argv.slice(2));

上述代码利用 fs.createReadStream 构建源文件的只读数据流,同时利用 fs.createWriteStream 构建目标文件的只写数据流,并通过 pipe 方法将二者串联。串联后的机制可形象地理解为水流经管道从一桶转移至另一桶。

Node.js目录遍历算法

目录遍历

目录遍历是文件操作中的常见需求。例如,编写程序以查找并处理指定目录下的所有 JS 文件时,必须遍历整个目录结构。

递归策略 目录遍历通常采用递归算法,这有助于保持代码简洁。递归算法与数学归纳法原理相似,通过不断缩减问题规模来求解。以下示例展示了该思路。


function factorial(n) {
  if (n === 1) {
    return 1;
  } else {
    return n * factorial(n - 1);
  }
}

上述函数用于计算 N 的阶乘(N!)。当 N 大于 1 时,问题转化为计算 N 乘以 N-1 的阶乘;当 N 等于 1 时,问题达到最小规模,无需进一步简化,直接返回 1。

注意事项: 尽管递归算法编写的代码简洁,但由于每次递归都会产生函数调用,在性能优先的场景下,应将递归算法转换为循环算法,以降低函数调用次数。

遍历策略 目录呈现树状结构,遍历时通常采用深度优先结合先序遍历的策略。深度优先指访问节点后优先遍历子节点而非邻居节点;先序遍历指首次到达某节点即视为该节点遍历完成,而非最后一次返回时才算。因此,使用该策略遍历下图所示树结构的顺序为 A > B > D > E > C > F。


     A
     / \
    B  C
    / \  \
   D  E  F

同步遍历 掌握必要算法后,可简单实现以下目录遍历函数。


function travel(dir, callback) {
  fs.readdirSync(dir).forEach(function (file) {
    var pathname = path.join(dir, file);

    if (fs.statSync(pathname).isDirectory()) {
      travel(pathname, callback);
    } else {
      callback(pathname);
    }
  });
}

可见,该函数以指定目录为遍历起点。遇到子目录时,优先继续遍历该子目录;遇到文件时,将文件的绝对路径传递给回调函数。回调函数获取路径后可执行各类判断与处理。假设存在如下目录结构:


- /home/user/
  - foo/
    x.js
  - bar/
    y.js
  z.css

使用以下代码遍历该目录时,输出结果如下。


travel('/home/user', function (pathname) {
  console.log(pathname);
});


/home/user/foo/x.js
/home/user/bar/y.js
/home/user/z.css

异步遍历 若读取目录或文件状态时采用异步 API,目录遍历函数的实现将更为复杂,但原理一致。travel 函数的异步版本如下。


function travel(dir, callback, finish) {
  fs.readdir(dir, function (err, files) {
    (function next(i) {
      if (i < files.length) {
        var pathname = path.join(dir, files[i]);

        fs.stat(pathname, function (err, stats) {
          if (stats.isDirectory()) {
            travel(pathname, callback, function () {
              next(i + 1);
            });
          } else {
            callback(pathname, function () {
              next(i + 1);
            });
          }
        });
      } else {
        finish && finish();
      }
    }(0));
  });
}

此处不深入探讨异步遍历函数的编写技巧,将在后续章节详细阐述。总之,可以看出异步编程具有一定的复杂性。

作者最新文章