nodejs fs学习
在本章的开始,我本来只想写一些关于fs模块的内容,虽然这个模块包含的方法非常的多,但没有想到的是它和我们上一篇文章Node.js Buffer还存在着关联,所以我又把关于Buffer的内容简单的学习下,以至于对它不那么陌生。我以为这样就可以来看看fs模块了,没想到又杀出个程咬金,这就是本文要学习的Stream。
这个Stream目前还是个不稳定的模块,从API的介绍来说,所有stream是对象EventEmitter的实例,测试下:
console.log(new(require(‘stream’)) instanceof require(‘events’).EventEmitter);//输出:true
什么是Stream(流)
在Node中,Stream是一个抽象的接口,由不同的实例对象来实现,比如HTTP服务器中的requestI流,再比如stdout标准输出流。流可以是可读的,可写的,或者可读可写的。
为什么要提供Stream
在Node中,大部分的模块能持续的读或者写,为了确保这些模块拥有统一的方法来完成持续的读或者写,Stream于是为它们提供了统一的接口。Stream接口提供了通用的方法和属性,因为stream是EventEmitter的实例,所以允许这些模块emit自己的事件。
Readable Stream(可读流)
一个常见的readable stream就是从一个文件中读取数据。可读流支持的事件有四种:data,end,error,close。它们分别对应如下场景:
data:当有数据可读时触发
end:当没有数据可读时或者达到文件尾
error:读取数据时遇到异常
close:底层的文件描述(fd)关闭时
为了简单起见,我们使用fs模块中的方法来读取windows下的boot.ini文件:
|
1
2
3
4
5
6
|
var fs = require('fs');fs.readFile('c:/boot.ini',function(err,data){ if(err) throw err; //console.log(data);//传递给回调函数的data参数是一个Buffer对象,大小默认为64K? console.log(data.toString());}); |
fs.readFile方法能触发data事件,在这里我们把读取到的数据打印到控制台上。为了让本文稍微有点深度,这里贴上fs.readFile方法的源码:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
fs.readFile = function(path, encoding_) { var encoding = typeof(encoding_) === 'string' ? encoding_ : null; var callback = arguments[arguments.length - 1]; if (typeof(callback) !== 'function') callback = function() {}; // first, stat the file, so we know the size. var size; var buffer; // single buffer with file data var buffers; // list for when size is unknown var pos = 0; var fd; fs.open(path, constants.O_RDONLY, 438 /*=0666*/, function(er, fd_) { if (er) return callback(er); fd = fd_; fs.fstat(fd, function(er, st) { if (er) return callback(er); size = st.size; if (size === 0) { // the kernel lies about many files. // Go ahead and try to read some bytes. buffers = []; return read(); } buffer = new Buffer(size); read(); }); }); function read() { if (size === 0) { buffer = new Buffer(8192); fs.read(fd, buffer, 0, 8192, -1, afterRead); } else { fs.read(fd, buffer, pos, size - pos, -1, afterRead); } } function afterRead(er, bytesRead) { if (er) { return fs.close(fd, function(er2) { return callback(er); }); } if (bytesRead === 0) { return close(); } pos += bytesRead; if (size !== 0) { if (pos === size) close(); else read(); } else { // unknown size, just read until we don't get bytes. buffers.push(buffer.slice(0, bytesRead)); read(); } } function close() { fs.close(fd, function(er) { if (size === 0) { // collected the data into the buffers list. buffer = Buffer.concat(buffers, pos); } else if (pos < size) { buffer = buffer.slice(0, pos); } if (encoding) buffer = buffer.toString(encoding); return callback(er, buffer); }); }}; |
想认真学习的人应该仔细读读fs.readFile是如何实现的,想看fs模块的完整源码位于lib目录下的fs.js文件。下载Node.js源码(版本v0.8.18)。
为了更加直观的描述readable stream中的这些事件,请参见下面的例子
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
var fs = require('fs');var opt = { flags:'r', encoding:'utf8', fd:null, mode:0666, bufferSize:64 * 1024, start:0, end:99};st = fs.createReadStream('c:/boot.ini',opt);//st.pause();//暂停data事件的触发//st.resume();//恢复data事件var text = "";st.on('data',function(data){ text += data;});st.on('end',function(close){ console.log("reading 99 bytes:\n" + text);});st.on('error',function(error){ console.log('An error occurred:' + error);});st.on('close',function(){ console.log('The file descriptor has been closed.');});st.on('open',function(fd){ console.log('Current fd number: ' + fd);}); |
输出:
Current fd number: 3
reading 99 bytes:
[boot loader]
timeout=3
default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
[operating systems]
The file descriptor has been closed.
F:\wamp\www\Node>
不想太多解析这个示例,为什么文件描述是3?因为0,1,2已经被占用啦,原因?你懂的!
Writable Stream(可写流)
可写流支持的事件有:drain,error,close,pipe。我只介绍drain和pipe,它们分别对应如下场景:
drain:当Node的写队列为空并且Buffer中没有数据时,发生这个事件
pipe:当把Writable Stream对象传递一个Readable Stream对象的pipe方法时发生
分别来看看这两个事件的例子:
1.drain
|
01
02
03
04
05
06
07
08
09
10
11
|
var fs = require('fs');var stream = fs.createWriteStream(__dirname + '/out.txt');var str = 'i=['; for (var i=0;i<10;i++){ stream.write(str + i + ']\r\n');} stream.on('drain',function(){ console.log('start drain');}); |
当for循环完成时,我们的控制台才打印出’start drain’,这意味着写队列已经为空,即当前没有事件存在写入的情况。由于drain事件能发生在任何时候,所以为了避免触发不必要的drain事件,官方的建议是,使用events.EventEmitter once()方法,这样drain事件只可能触发一次,尔后它上面的监听器就被删除了。
2.pipe
|
1
2
3
4
5
6
7
8
|
var fs = require('fs');var writeStream = fs.createWriteStream('copyout.txt',{ flags:'w'});var readStream = fs.createReadStream('./out.txt');readStream.pipe(writeStream);writeStream.on('close',function(){ console.log('All done!');}); |
这个示例把out.txt文件中的内容复制到了copyout.txt中。
这篇文章写了我很长时间,虽然还有很多的知识点没有介绍上来,也到此为止吧。我希望在日后有机会补充一些。
Date.prototype.format = function(format){
var o = {
"M+" : this.getMonth()+1, //month
"d+" : this.getDate(), //day
"h+" : this.getHours(), //hour
"m+" : this.getMinutes(), //minute
"s+" : this.getSeconds(), //second
"q+" : Math.floor((this.getMonth()+3)/3), //quarter
"S" : this.getMilliseconds() //millisecond
}
if(/(y+)/.test(format)) format=format.replace(RegExp.$1,
(this.getFullYear()+"").substr(4 - RegExp.$1.length));
for(var k in o)if(new RegExp("("+ k +")").test(format))
format = format.replace(RegExp.$1,
RegExp.$1.length==1 ? o[k] :
("00"+ o[k]).substr((""+ o[k]).length));
return format;
}
var fs=require('fs');
fs.open(__dirname+'/error.log','a',function(err,fd){
if(err){ throw err; }
cTime = new Date();
cTime = cTime.format('yyyy-MM-dd h:m:s');
var wBuff = new Buffer('Start log: ' + cTime + "\r\n");
buffPos = 0;
buffLen = wBuff.length;
filePos = 0;
fs.write(fd,wBuff,buffPos,buffLen,filePos,function(err,wbytes,data){
if(err){ throw err; }
console.log('wrote ' + wbytes + ' bytes');
fs.close(fd);
});
});
nodejs fs学习的更多相关文章
- # nodejs模块学习: express 解析
# nodejs模块学习: express 解析 nodejs 发展很快,从 npm 上面的包托管数量就可以看出来.不过从另一方面来看,也是反映了 nodejs 的基础不稳固,需要开发者创造大量的轮子 ...
- nodejs fs path
内容详见我的gitHub: https://github.com/shangyueyue/ssy-utils/tree/master/src/nodejs/fs
- nodejs模块学习: webpack
nodejs模块学习: webpack nodejs 发展很快,从 npm 上面的包托管数量就可以看出来.不过从另一方面来看,也是反映了 nodejs 的基础不稳固,需要开发者创造大量的轮子来解决现实 ...
- nodejs基础学习1
ES6常用新语法 ES6新语法 什么是ES6? 由于JavaScript是上个世纪90年代,由Brendan Eich在用了10天左右的时间发明的:虽然语言的设计者很牛逼,但是也扛不住"时间 ...
- NodeJS入门学习教程
一.概念 1.什么是nodejs Node.js是JavaScript 运行时环境,通俗易懂的讲,Node.js是JavaScript的运行平台 Node.js既不是语言,也不是框架,它是一个平台 2 ...
- NodeJs入门学习(一)
NodeJs是针对前端工程师向web后端深入理解的一门很好的语言. 首先,记录NodeJS几大特性,后续补充: 一.Node.js 是单进程单线程应用程序,但是通过事件和回调支持并发,所以性能非常高. ...
- NodeJs开发学习目录
1.Nodejs基本概念及Nodejs.npm安装测试[2014-06-06] 2.开发工具简介(主要介绍Sublime Text使用) [2014-06-06] 3.Sublime text插件安装 ...
- nodejs模块学习: connect解析
nodejs模块学习: connect解析 nodejs 发展很快,从 npm 上面的包托管数量就可以看出来.不过从另一方面来看,也是反映了 nodejs 的基础不稳固,需要开发者创造大量的轮子来解决 ...
- nodejs模块学习: connect2解析
nodejs模块学习: connect2 解析 nodejs 发展很快,从 npm 上面的包托管数量就可以看出来.不过从另一方面来看,也是反映了 nodejs 的基础不稳固,需要开发者创造大量的轮子来 ...
随机推荐
- NPOI 1.2教程(目录)操作Excel
原文地址:http://www.cnblogs.com/atao/archive/2009/11/15/1603528.html
- java-web 过滤器 & 监听器 & 拦截器
Tomcat 的容器分为四个等级.真正管理 Servlet 的容器是 Context 容器,一个 Context 对应一个 Web 工程.在 Tomcat 的配置文件里能够非常easy发现这一点.例如 ...
- MYSQL批量插入数据库实现语句性能分析【转】 批量插入!程序里面对于数据库插入的功能尽量用【异步处理+批量插入+(事务)】
假定我们的表结构如下 代码如下 CREATE TABLE example (example_id INT NOT NULL,name VARCHAR( 50 ) NOT NULL,value VA ...
- Android无线测试之—UiAutomator UiScrollable API介绍三
获取列表子元素 一.相关API介绍 返回值 API 描述 UiObject getChildByDescription(UiSelector childPattern, String text, bo ...
- Servlet及相关类和接口
上一篇介绍了在Web项目中web.xml文件的配置信息,本篇主要介绍里面非常重要的配置——Servlet配置,重点介绍与Servlet相关的几个接口和类,包括Servlet接口.ServletConf ...
- 【bzoj4518】[Sdoi2016]征途 斜率优化dp
原文地址:http://www.cnblogs.com/GXZlegend/p/6812435.html 题目描述 Pine开始了从S地到T地的征途. 从S地到T地的路可以划分成n段,相邻两段路的分界 ...
- (转)SPDY
SPDY:Google开发的基于传输控制协议(TCP)的应用层协议,目前已经被用于Google Chrome浏览器中来访问Google的SSL加密服务.SPDY并不是一种用于替代HTTP的协议,而是对 ...
- Minecraft Forge编程入门一 “环境搭建”
什么是Forge Minecraft Forge is a Minecraft application programming interface (API) which allows almost ...
- ASP.Net请求处理机制初步探索之旅 - Part 3 管道(转)
开篇:上一篇我们了解了一个ASP.Net页面请求的核心处理入口,它经历了三个重要的入口,分别是:ISAPIRuntime.ProcessRequest().HttpRuntime.ProcessReq ...
- Access 2010 VBA 读取 表中的数据
Option Compare Database Private Sub Command0_Click() Dim db Dim rs As Recordset Dim str As String Se ...