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 相关文章推荐
jquery JSON的解析方式
Jul 25 Javascript
基于Jquery的动态添加控件并取值的实现代码
Sep 24 Javascript
jquery $.each 和for怎么跳出循环终止本次循环
Sep 27 Javascript
jquery在项目中做复选框时遇到的一些问题笔记
Nov 17 Javascript
JS原型链怎么理解
Jun 27 Javascript
AngularJS实现根据变量改变动态加载模板的方法
Nov 04 Javascript
Angular 4依赖注入学习教程之ValueProvider的使用(七)
Jun 04 Javascript
详解React开发必不可少的eslint配置
Feb 05 Javascript
详解小程序设置缓存并且不覆盖原有数据
Apr 15 Javascript
JS中的一些常用的函数式编程术语
Jun 15 Javascript
js实现的在本地预览图片功能示例
Nov 09 Javascript
JS实现简单打字测试
Jun 24 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
深入array multisort排序原理的详解
2013/06/18 PHP
PHP消息队列用法实例分析
2016/02/12 PHP
基于jquery实现的可以编辑选择的下拉框的代码
2010/11/19 Javascript
js 优化次数过多的循环 考虑到性能问题
2011/03/05 Javascript
jquery选择器、属性设置用法经验总结
2013/09/08 Javascript
jquery分页插件jpaginate在IE中不兼容问题
2014/04/22 Javascript
javascript实现的一个带下拉框功能的文本框
2014/05/08 Javascript
重写document.write实现无阻塞加载js广告(补充)
2014/12/12 Javascript
JavaScript中数组继承的简单示例
2015/07/29 Javascript
JS+CSS实现自适应选项卡宽度的圆角滑动门效果
2015/09/15 Javascript
JavaScript文档碎片操作实例分析
2015/12/12 Javascript
jQuery 实现评论等级好评差评特效
2016/05/06 Javascript
angularjs封装bootstrap时间插件datetimepicker
2016/06/20 Javascript
如何使用bootstrap框架 bootstrap入门必看!
2017/04/13 Javascript
angular+webpack2实战例子
2017/05/23 Javascript
js canvas实现写字动画效果
2018/11/30 Javascript
[02:12]DOTA2英雄基础教程 变体精灵
2013/12/16 DOTA
python将xml xsl文件生成html文件存储示例讲解
2013/12/03 Python
浅谈编码,解码,乱码的问题
2016/12/30 Python
浅谈Python中的bs4基础
2018/10/21 Python
django xadmin action兼容自定义model权限教程
2020/03/30 Python
Python使用itcaht库实现微信自动收发消息功能
2020/07/13 Python
CSS3 @keyframes简单动画实现
2018/02/24 HTML / CSS
摩顿布朗英国官方网上商店:奢华沐浴、身体和头发护理
2016/10/29 全球购物
丹麦优惠购物网站:PLUSSHOP
2019/03/24 全球购物
Brora官网:英国领先的羊绒服装品牌
2019/08/28 全球购物
毕业生找工作的求职信范文
2013/12/24 职场文书
3.12植树节活动总结2014
2014/03/13 职场文书
如何写自我鉴定
2014/03/19 职场文书
行政文员实习自我鉴定范文
2014/09/14 职场文书
职工年度考核评语
2014/12/31 职场文书
2015年中个人总结范文
2015/03/10 职场文书
初中政治教学工作总结
2015/08/13 职场文书
HDFS免重启挂载新磁盘
2022/04/06 Servers
vue如何实现关闭对话框后刷新列表
2022/04/08 Vue.js
Spring Boot项目如何优雅实现Excel导入与导出功能
2022/06/10 Java/Android