于是,node中的文件操作给了我们光,这次就简单的来说说关于 node 中 File System的那档子事,本文主要包括了点操作文件的API,以及比较给力的 Stream 操作~,好了那么菜鸟留步,大牛请轻功飘过~
File System
关于文件操作,那么这边主要的就是 fs 这个模块。对于node中 fs 模块提供的API很多,但是其所有的方法均有同步和异步的形式。对于读取文件内容来说,最需要注意的一点就是异步与同步之间控制执行流程的问题~
-
var fs = require('fs');
-
// 使用异步回调的方式 因为是异步的,所以对于数据读取完后的操作我们需要使用回调的方式进行处理
-
// 这点对于习惯事件操作的前端开发应该是习以为常的 。
-
fs.readFile('data.json',function(err, data){
-
if(err){ }else{
-
console.log(data.length);
-
}
-
});
结果:
每个异步的API,都有其回调函数可以使用,那么对于下面的方式就会报错,就犹如在JS使用的setTimeout等类似,
-
var fs = require('fs');
-
//会有错 因为是异步读取文件 ,在console的时候数据还未读取出来
-
var data = fs.readFile('data.json');
-
console.log(data.length);
结果:
或者干脆直接 使用其对应的同步API使用
-
var fs = require('fs');
-
// 或者改为同步的API读取
-
var data = fs.readFileSync('data.json');
-
console.log(data.length);
结果:
其他一些简单的API
-
fs.writeFile('delete.txt','1234567890',function(err){
-
console('youxi!');
-
});
-
-
// 删除文件
-
fs.unlink('delete.txt', function(){
-
console.log('success');
-
});
-
-
// 修改文件名称
-
fs.rename('delete.txt','anew.txt',function(err){
-
console.log('rename success');
-
-
// 查看文件状态
-
fs.stat('anew.txt', function(err, stat){
-
console.log(stat);
-
});
-
});
-
-
// 判断文件是否存在
-
fs.exists('a.txt', function( exists ){
-
console.log( exists );
-
});
File System API
fs.open( path, flags, [mode], callback );
flags为:
'r' - 以只读方式打开文件,当文件不存在的时候发生异常
'r+' - 以读写方式打开文件,当文件不存在的时候发生异常
'rs' - 以只读方式同步打开文件,绕过本地缓存文件进行系统操作
'rs+' - 以读写方式同步打开文件 ,绕过本地缓存文件进行系统操作
'w' - 以只写方式打开文件,当文件不存在时创建文件,或者存在的时候覆盖文件
'wx' - 与'w'一致,但只适用于文件不存在的时候( 测试的时候,,node版本为v0.10.26,如果文件不存在则正常创建文件并且写入数据,但是当文件不存在的时候,报错为必须要写上callback,加上callback后不报错但是不执行任何操作。 )
'w+' - 以读写方式打开文件
'ws+' - 与'w+'一致,但只适用于文件不存在的时候
'a' - 以添加数据的方式打开文件,文件不存在则创建
'a+' - 以添加和读取的方式打开文件,文件不存在则创建
'ax+' - 与'a+'一致,但是存在的时候会失败
mode为:
设置文件的模式,默认为 0666,可读可写。
callback:
给出了两个参数 ( err, fd )
fs.readFile( filename, [optins], callback );
filename : String
option : Object
encoding : String | Null, default = Null
flag : String default = 'r'
callback : Function
// callback 具有两个参数,( err, data ),和node中大部分回调接口类似。
fs.writeFile( filename, data, [optins], callback );
filename : String
data : String | Buffer(之后会简单介绍)
option : Object
encoding : String | Null, default = 'utf8'
mode : Number default = 438
flag : String default = 'w'
callback : Function
// 将数据添加到文件末尾
fs.appendFile( filename, data, [optins], callback );
filename : String
data : String | Buffer
option : Object
encoding : String | Null, default = 'utf8'
mode : Number default = 438
flag : String default = 'w'
callback : Function
以上比较常用的异步API 均有与之对应的同步API,在异步API后面加上Sync即是同步的API。更多的API请查看官方文档
Stream
Stream可以算是node里的一出重头戏,与大数据处理方面有密切不可分的关系。
-
var fs = require('fs');
-
function copy( src, dest ){
-
fs.writeFileSync( dest, fs.readFileSync(src) );
-
}
-
copy('data.json', 'dataStream.json');
上面是一个对文件拷贝的代码,看似没什么问题,也的确在处理小文件的时候没什么大问题,但是一旦处理数量级很大的文件的时候可以看出,先将数据读取出来,在写入,内存作为中转,如果文件太大就会产生问题。
在需要处理大文件的情况时,便要使用file system的另外几个API,createReadStream和fs.createWriteStream,将文件作为一块一块小的数据流进行处理,而不是一整块大型数据。
-
// 也可能出现内存爆仓 写入数据跟不上读取速度 一直读取的文件不断放入内存中
-
// 但是两个操作速度绝对是不一样的,于是未被写入的数据在内存中不断变大,就可能会导致内存的爆仓。
-
var fs = require('fs');
-
var rs = fs.createReadStream('data.json');
-
var ws = fs.createWriteStream('dataStream.json')
-
rs.on('data',function(chunk){
-
console.log('data chunk read ok');
-
times++;
-
ws.write(chunk,function(){
-
console.log('data chunk write ok');
-
});
-
});
-
rs.on('end',function(){
-
console.log(times);
-
});
结果:
这边可以看出对于读取和写入的速度不同,在最开始的时候读取完2块数据块后,第一块数据块写入才完毕,当累计很多之后势必会造成内存问题。所以对于流操作还需要改进一下
-
var fs = require('fs');
-
var rs = fs.createReadStream('data.json');
-
var ws = fs.createWriteStream('dataStream.json')
-
// eg 1 可以看数据流 drain事件表示为 只写数据流已将缓存里的数据写入目标
-
rs.on('data',function(chunk){
-
console.log('data chunk read ok');
-
if( ws.write(chunk,function(){
-
console.log('data chunk write ok')
-
}) == false ){
-
rs.pause()
-
}
-
});
-
rs.on('end',function(){
-
ws.end()
-
});
-
ws.on('drain',function(){
-
rs.resume();
-
});
结果:
或者:
-
//eg2
-
function reStartRead( rs ){
-
console.log('lasted readed data write OK, reStart Read.');
-
rs.resume();
-
}
-
rs.on('data',function(chunk){
-
console.log('data chunk read ok' );
-
if( ws.write(chunk,function(){
-
reStartRead( rs );
-
}) == false ){
-
rs.pause()
-
}
-
});
-
rs.on('end',function(){
-
ws.end()
-
});
结果:
上面两个方式相当于每次读取完一块data块之后便暂停读取,直到该块写入已经完成才再次开启读取的stream。
对于这种情况 node里面有一个pipe的方法 连接两个数据流,犹如导管一样将数据读入写入
-
function copy( src, dest ){
-
fs.createReadStream( src ).pipe( fs.createWriteStream( dest ) );
-
}
-
copy('data.json', 'dataStream.json');
上面使用到了 stream 里面的一些方法和事件,更多API内容请查看官网