9、NodeJs中readFile、writeFile和Stream流操作文件的区别

阅读() @2018-07-15 14:13:42

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

'use strict'

var fs = require('fs');

var source = fs.readFileSync('1.txt', 'utf8');//读取1.txt中的内容
fs.writeFileSync('2.txt', source);//将source资源写入2.txt文件

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

流的概念

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

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

'use strict'

var fs = require('fs');

var rs = fs.createReadStream('1.txt');
var ws = fs.createWriteStream('2.txt');

rs.on('data', function(chunk){
    ws.write(chunk);
});

rs.on('end', function(){
    console.log('end');
    ws.end();
});

rs.on('error', function(error){
    console.log(error);
});

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

'use strict'

var fs = require('fs');

var rs = fs.createReadStream('1.txt');
var ws = fs.createWriteStream('2.txt');

rs.on('data', function(chunk){
    if( ws.write(chunk)===false ){
        rs.pause();//如果上一段读取的内容还未写完,则暂停读取
    }
});

ws.on('train', function(){
    rs.resume();//上一段内容全部写入完毕之后,开始继续读取
});

rs.on('end', function(){
    console.log('end');
    ws.end();
});

rs.on('error', function(error){
    console.log(error);
});

或者直接使用pipe方法:

'use strict'

var fs = require('fs');

fs.createReadStream('1.txt').pipe( fs.createWriteStream('2.txt') );

下面是一个更加完整的读写过程,我用的是一个364M的视频做测试:

'use strict'

var fs = require('fs');

var rs = fs.createReadStream('v1.wmv');//读取文件
var ws = fs.createWriteStream('v2.wmv');//写入文件
var v1TotalSize = fs.statSync('v1.wmv').size;//文件总大小
var v1TmpSize = 0;//已读取的文件大小
var startTime = Date.now();//开始时的时间戳

rs.on('data', function(chunk){
    v1TmpSize += chunk.length;
    if(ws.write(chunk)===false){
        rs.pause();
    }
});

rs.on('end', function(){
    ws.end();
    clearInterval(timer);
});

ws.on('drain', function(){
    rs.resume();
});

//实时获取文件读取和写入的状态:
function computing(){
    var timeDiff = Math.ceil(Date.now() - startTime);
    var sizeDiff = Math.ceil(v1TotalSize-v1TmpSize)/1000000;
    console.log('用时:'+timeDiff+'s'+' 还剩:'+sizeDiff+'M'+' 速度:'+Math.ceil(sizeDiff/timeDiff)+'M/S');
}

var timer = setInterval(computing, 10);

拷贝以上代码,直接替换视频文件名,即可在本地运行!

微信二维码