深入nodejs中流(stream)的理解

 更新时间:2017年03月27日 09:51:16   作者:chshouyu  
本篇文章主要介绍了深入nodejs中流(stream)的理解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

nodejs的fs模块并没有提供一个copy的方法,但我们可以很容易的实现一个,比如:

var source = fs.readFileSync('/path/to/source', {encoding: 'utf8'});
fs.writeFileSync('/path/to/dest', source);

这种方式是把文件内容全部读入内存,然后再写入文件,对于小型的文本文件,这没有多大问题,比如grunt-file-copy就是这样实现的。但是对于体积较大的二进制文件,比如音频、视频文件,动辄几个GB大小,如果使用这种方法,很容易使内存“爆仓”。理想的方法应该是读一部分,写一部分,不管文件有多大,只要时间允许,总会处理完成,这里就需要用到流的概念。

如上面高大上的图片所示,我们把文件比作装水的桶,而水就是文件里的内容,我们用一根管子(pipe)连接两个桶使得水从一个桶流入另一个桶,这样就慢慢的实现了大文件的复制过程。

Stream在nodejs中是EventEmitter的实现,并且有多种实现形式,例如:

  • http responses request
  • fs read write streams
  • zlib streams
  • tcp sockets
  • child process stdout and stderr

上面的文件复制可以简单实现一下:

var fs = require('fs');
var readStream = fs.createReadStream('/path/to/source');
var writeStream = fs.createWriteStream('/path/to/dest');

readStream.on('data', function(chunk) { // 当有数据流出时,写入数据
  writeStream.write(chunk);
});

readStream.on('end', function() { // 当没有数据时,关闭数据流
  writeStream.end();
});

上面的写法有一些问题,如果写入的速度跟不上读取的速度,有可能导致数据丢失。正常的情况应该是,写完一段,再读取下一段,如果没有写完的话,就让读取流先暂停,等写完再继续,于是代码可以修改为:

var fs = require('fs');
var readStream = fs.createReadStream('/path/to/source');
var writeStream = fs.createWriteStream('/path/to/dest');

readStream.on('data', function(chunk) { // 当有数据流出时,写入数据
  if (writeStream.write(chunk) === false) { // 如果没有写完,暂停读取流
    readStream.pause();
  }
});

writeStream.on('drain', function() { // 写完后,继续读取
  readStream.resume();
});

readStream.on('end', function() { // 当没有数据时,关闭数据流
  writeStream.end();
});

或者使用更直接的pipe

// pipe自动调用了data,end等事件
fs.createReadStream('/path/to/source').pipe(fs.createWriteStream('/path/to/dest'));

下面是一个更加完整的复制文件的过程

var fs = require('fs'),
  path = require('path'),
  out = process.stdout;

var filePath = '/Users/chen/Movies/Game.of.Thrones.S04E07.1080p.HDTV.x264-BATV.mkv';

var readStream = fs.createReadStream(filePath);
var writeStream = fs.createWriteStream('file.mkv');

var stat = fs.statSync(filePath);

var totalSize = stat.size;
var passedLength = 0;
var lastSize = 0;
var startTime = Date.now();

readStream.on('data', function(chunk) {

  passedLength += chunk.length;

  if (writeStream.write(chunk) === false) {
    readStream.pause();
  }
});

readStream.on('end', function() {
  writeStream.end();
});

writeStream.on('drain', function() {
  readStream.resume();
});

setTimeout(function show() {
  var percent = Math.ceil((passedLength / totalSize) * 100);
  var size = Math.ceil(passedLength / 1000000);
  var diff = size - lastSize;
  lastSize = size;
  out.clearLine();
  out.cursorTo(0);
  out.write('已完成' + size + 'MB, ' + percent + '%, 速度:' + diff * 2 + 'MB/s');
  if (passedLength < totalSize) {
    setTimeout(show, 500);
  } else {
    var endTime = Date.now();
    console.log();
    console.log('共用时:' + (endTime - startTime) / 1000 + '秒。');
  }
}, 500);

可以把上面的代码保存为copy.js试验一下

我们添加了一个递归的setTimeout(或者直接使用setInterval)来做一个旁观者,每500ms观察一次完成进度,并把已完成的大小、百分比和复制速度一并写到控制台上,当复制完成时,计算总的耗费时间,效果如图:

我们复制了一集1080p的权利的游戏第四季第7集,大概3.78G大小,由于使用了SSD,可以看到速度还是非常不错的,哈哈哈~ 复制完成后,显示总花费时间

结合nodejs的readlineprocess.argv等模块,我们可以添加覆盖提示、强制覆盖、动态指定文件路径等完整的复制方法,有兴趣的可以实现一下,实现完成,可以

ln -s /path/to/copy.js /usr/local/bin/mycopy

这样就可以使用自己写的mycopy命令替代系统的cp命令

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • 详解node服务器中打开html文件的两种方法

    详解node服务器中打开html文件的两种方法

    本篇文章主要介绍了详解node服务器中打开html文件的两种方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-09-09
  • Nodejs如何解决跨域(CORS)

    Nodejs如何解决跨域(CORS)

    这篇文章主要介绍了Nodejs如何解决跨域(CORS)问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-01-01
  • nodejs+express最简易的连接数据库的方法

    nodejs+express最简易的连接数据库的方法

    这篇文章主要介绍了nodejs+express 最简易的连接数据库,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • node.js去水印方法实例分析

    node.js去水印方法实例分析

    这篇文章主要介绍了node.js去水印方法,结合实例形式分析了node.js基于第三方平台实现去水印的相关交互与操作技巧,需要的朋友可以参考下
    2023-04-04
  • 详解Node.js使用token进行认证的简单示例

    详解Node.js使用token进行认证的简单示例

    这篇文章主要介绍了详解Node.js使用token进行认证的简单示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05
  • 异步JavaScript编程中的Promise使用方法

    异步JavaScript编程中的Promise使用方法

    这篇文章主要介绍了异步JavaScript编程中的Promise使用方法,包含Ajax的结合操作等问题,需要的朋友可以参考下
    2015-07-07
  • windows 下安装nodejs 环境变量设置

    windows 下安装nodejs 环境变量设置

    windows 下安装nodejs 了,也安装了npm, 但是有时候切不能直接用request(‘ws’)这一类的东西.我觉得是确实环境变量或其他设置有问题,能否给个完整的设置方案:
    2017-02-02
  • nodejs实现超简单生成二维码的方法

    nodejs实现超简单生成二维码的方法

    这篇文章主要介绍了nodejs实现超简单生成二维码的方法,结合实例形式分析了nodejs基于qr-image插件生成二维码的相关操作技巧,需要的朋友可以参考下
    2018-03-03
  • 浅谈Nodejs中的作用域问题

    浅谈Nodejs中的作用域问题

    在JS中有全局作用域和函数作用域,而在Nodejs中也自己的作用域,分为全局作用域(global)和模块作用域。本文将对Nodejs中的作用域进行介绍,需要的朋友一起来看下吧
    2016-12-12
  • 如何从0开始用node写一个自己的命令行程序

    如何从0开始用node写一个自己的命令行程序

    这篇文章主要介绍了如何从0开始用node写一个自己的命令行程序,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-12-12

最新评论