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


Posted in Javascript onFebruary 16, 2016

文件拷贝
NodeJS 提供了基本的文件操作 API,但是像文件拷贝这种高级功能就没有提供,因此我们先拿文件拷贝程序练手。与 copy 命令类似,我们的程序需要能接受源文件路径与目标文件路径两个参数。

小文件拷贝
我们使用 NodeJS 内置的 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] 固定等于 NodeJS 执行程序的绝对路径,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 方法把两个数据流连接了起来。连接起来后发生的事情,说得抽象点的话,水顺着水管从一个桶流到了另一个桶。

遍历目录

遍历目录是操作文件时的一个常见需求。比如写一个程序,需要找到并处理指定目录下的所有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));
  });
}

这里不详细介绍异步遍历函数的编写技巧,在后续章节中会详细介绍这个。总之我们可以看到异步编程还是蛮复杂的。

Javascript 相关文章推荐
疯掉了,尽然有js写的操作系统
Apr 23 Javascript
js文字滚动停顿效果代码
Jun 28 Javascript
JS常用知识点整理
Jan 21 Javascript
关于axios如何全局注册浅析
Jan 14 Javascript
使用js实现将后台传入的json数据放在前台显示
Aug 06 Javascript
angularJs select绑定的model取不到值的解决方法
Oct 08 Javascript
socket io与vue-cli的结合使用的示例代码
Nov 01 Javascript
vue v-for循环重复数据无法添加问题解决方法【加track-by='索引'】
Mar 15 Javascript
巧妙运用v-model实现父子组件传值的方法示例
Apr 07 Javascript
微信小程序实现分享商品海报功能
Sep 30 Javascript
jQuery+PHP+Ajax实现动态数字统计展示功能
Dec 25 jQuery
vue elementui 实现搜索栏公共组件封装的实例代码
Jan 20 Javascript
详解Node.js包的工程目录与NPM包管理器的使用
Feb 16 #Javascript
javascript每日必学之运算符
Feb 16 #Javascript
解析Node.js基于模块和包的代码部署方式
Feb 16 #Javascript
javascript每日必学之基础入门
Feb 16 #Javascript
快速掌握Node.js环境的安装与运行方法
Feb 16 #Javascript
js实现异步循环实现代码
Feb 16 #Javascript
JavaScript实现跑马灯抽奖活动实例代码解析与优化(二)
Feb 16 #Javascript
You might like
不支持fsockopen但支持culr环境下下ucenter与modoer通讯问题
2011/08/12 PHP
php新浪微博登录接口用法实例
2014/12/23 PHP
PHP框架性能测试报告
2016/05/08 PHP
总结PHP中数值计算的注意事项
2016/08/14 PHP
php 生成加密公钥加密私钥实例详解
2017/06/16 PHP
phpstudy后门rce批量利用脚本的实现
2019/12/12 PHP
js数组Array sort方法使用深入分析
2013/02/21 Javascript
Javascript中的异步编程规范Promises/A详细介绍
2014/06/06 Javascript
Javascript学习笔记之数组的遍历和 length 属性
2014/11/23 Javascript
JQuery仿小米手机抢购页面倒计时效果
2014/12/16 Javascript
jQuery中not()方法用法实例
2015/01/06 Javascript
简介JavaScript中的unshift()方法的使用
2015/06/09 Javascript
基于JavaScript实现图片点击弹出窗口而不是保存
2016/02/06 Javascript
浅谈window.onbeforeunload() 事件调用ajax
2016/06/29 Javascript
canvas快速绘制圆形、三角形、矩形、多边形方法介绍
2016/12/29 Javascript
JS实现全屏的四种写法
2016/12/30 Javascript
bootstrap中日历范围选择插件daterangepicker的使用详解
2018/04/17 Javascript
微信小程序实现发红包功能
2018/07/11 Javascript
Vue2.0实现组件之间数据交互和通信操作示例
2019/05/16 Javascript
JavaScript实现放大镜效果代码示例
2020/04/29 Javascript
js实现鼠标切换图片(无定时器)
2021/01/27 Javascript
[00:15]TI9地铁玩家打卡
2019/08/11 DOTA
python写入中英文字符串到文件的方法
2015/05/06 Python
动态规划之矩阵连乘问题Python实现方法
2017/11/27 Python
python3结合openpyxl库实现excel操作的实例代码
2018/09/11 Python
使用python判断jpeg图片的完整性实例
2019/06/10 Python
在Pycharm中使用GitHub的方法步骤
2019/06/13 Python
Numpy 中的矩阵求逆实例
2019/08/26 Python
Flask框架请求钩子与request请求对象用法实例分析
2019/11/07 Python
使用pytorch 筛选出一定范围的值
2020/06/28 Python
手术室护士自我鉴定
2013/10/14 职场文书
小学生家长评语集锦
2014/01/30 职场文书
校园游戏活动新闻稿
2014/10/15 职场文书
乡镇计划生育工作汇报
2014/10/28 职场文书
酒会邀请函
2015/01/31 职场文书
CSS list-style-type属性使用方法
2023/05/21 HTML / CSS