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的readline
, process.argv
等模块,我们可以添加覆盖提示、强制覆盖、动态指定文件路径等完整的复制方法,有兴趣的可以实现一下,实现完成,可以
ln -s /path/to/copy.js /usr/local/bin/mycopy
这样就可以使用自己写的mycopy
命令替代系统的cp
命令
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
稳了!魔兽国服回归的3条重磅消息!官宣时间再确认!
昨天有一位朋友在大神群里分享,自己亚服账号被封号之后居然弹出了国服的封号信息对话框。
这里面让他访问的是一个国服的战网网址,com.cn和后面的zh都非常明白地表明这就是国服战网。
而他在复制这个网址并且进行登录之后,确实是网易的网址,也就是我们熟悉的停服之后国服发布的暴雪游戏产品运营到期开放退款的说明。这是一件比较奇怪的事情,因为以前都没有出现这样的情况,现在突然提示跳转到国服战网的网址,是不是说明了简体中文客户端已经开始进行更新了呢?
更新日志
- 谭艳《遗憾DSD》2023 [WAV+CUE][1G]
- Beyond2024《真的见证》头版限量编号MQA-UHQCD[WAV+CUE]
- 瑞鸣唱片2024-《荒城之月》SACD传统民谣[ISO]
- 好薇2024《兵哥哥》1:124K黄金母盘[WAV+CUE]
- 胡歌.2006-珍惜(EP)【步升大风】【FLAC分轨】
- 洪荣宏.2014-拼乎自己看【华特】【WAV+CUE】
- 伊能静.1999-从脆弱到勇敢1987-1996精选2CD【华纳】【WAV+CUE】
- 刘亮鹭《汽车DJ玩主》[WAV+CUE][1.1G]
- 张杰《最接近天堂的地方》天娱传媒[WAV+CUE][1.1G]
- 群星《2022年度发烧天碟》无损黑胶碟 2CD[WAV+CUE][1.4G]
- 罗文1983-罗文甄妮-射雕英雄传(纯银AMCD)[WAV+CUE]
- 群星《亚洲故事香港纯弦》雨果UPMAGCD2024[低速原抓WAV+CUE]
- 群星《经典咏流传》限量1:1母盘直刻[低速原抓WAV+CUE]
- 庾澄庆1993《老实情歌》福茂唱片[WAV+CUE][1G]
- 许巍《在别处》美卡首版[WAV+CUE][1G]