Open libin1991 opened 6 years ago
如果读取一个文件,使用fs.readFileSync同步读取,程序会被阻塞,然后所有数据被写到内存中。使用fs.readFile读取,程序不会阻塞,但是所有数据依旧会一次性全被写到内存,然后再让消费者去读取。如果文件很大,内存使用便会成为问题。 这种情况下流就比较有优势。流相比一次性写到内存中,它会先写到到一个缓冲区,然后再由消费者去读取,不用将整个文件写进内存,节省了内存空间。
fs.readFileSync
fs.readFile
Node.js中有四种基本类型的流:
createReadStream实现了stream.Readable接口的对象,将对象数据读取为流数据,当监听data事件后,开始发射数据
createReadStream
stream.Readable
var util = require('util'); var fs = require("fs") fs.createReadStream = function(path, options) { return new ReadStream(path, options); }; util.inherits(ReadStream, Readable);
var rs = fs.createReadStream(path,[options]); 1.path 读取文件的路径 2.options flags打开文件要做的操作,默认为'r' encoding默认为null start开始读取的索引位置 end结束读取的索引位置(包括结束位置) highWaterMark读取缓存区默认的大小64kb > 如果指定utf8编码highWaterMark要大于3个字节
// 与指定{encoding:'utf8'}效果相同,设置编码 rs.setEncoding('utf8');
// 一旦监听data事件时,流就可以读文件的内容,并且发射data。 // 根据设置的读取缓存区默认大小来决定,读一段发射一段,直到读完。 // 默认情况下,监听data事件后会不停的读数据,然后出发data事件,触发完data事件后,再次读数据。不会停。 // 希望流有一个暂停和恢复触发机制,见4.8 暂停和恢复触发data rs.on('data', function (data) { console.log(data); });
// 文件读完了,会触发end事件 rs.on('end', function () { console.log('读取完成'); });
// 文件读取出错了,会触发error事件 rs.on('error', function () { console.log("error"); });
// 如果是文件流还会涉及到open和close两个事件 rs.on('open', function () { console.log("文件打开"); });
// 如果是文件流还会涉及到open和close两个事件 rs.on('close', function () { console.log("文件关闭"); });
// 通过pause()方法和resume()方法 rs.on('data', function (data) { console.log(data); rs.pause(); // 暂停读取和发射data事件 setTimeout(function () { rs.resume(); // 恢复读取并触发data事件 },2000); });
以上可以看到:
open
data
end
close
可读流事实上工作在下面两种模式之一:flowing 和 paused
flowing
paused
在 flowing 模式下, 可读流自动从系统底层读取数据,并通过 EventEmitter 接口的事件尽快将数据提供给应用。
EventEmitter
在 paused 模式下,必须显式调用 stream.read() 方法来从流中读取数据片段。
stream.read()
所有初始工作模式为 paused 的 Readable 流,可以通过下面三种途径切换到 flowing 模式:
Readable
'data'
var rs = fs.createReadStream(path,[options]);
stream.resume()
stream.pipe()
Writable
可读流可以通过下面途径切换到 paused 模式:
stream.pause()
stream.unpipe()
如果 Readable 切换到 flowing 模式,且没有消费者处理流中的数据,这些数据将会丢失。 比如, 调用了 readable.resume() 方法却没有监听 'data' 事件,或是取消了 'data' 事件监听,就有可能出现这种情况。
createWriteStream实现了stream.Writable接口的对象将流数据写入到对象中
createWriteStream
stream.Writable
fs.createWriteStream = function(path, options) { return new WriteStream(path, options); }; util.inherits(WriteStream, Writable);
// 往可写流里写数据时,不会立刻写入文件的,而会先写入缓存区, 缓存区大小就是highWaterMark,默认16k。然后等缓存区满了之后再真正的写入文件里。 var ws = fs.createWriteStream(path,[options]); 1. path写入的文件路径 2. options flags 打开文件要做的操作,默认为'w' encoding 默认为utf8 start 开始位置 highWaterMark 写入缓存区的默认大小16kb
// write方法有返回值flag,按理说返回false就不能往里面写了,但是真的写了数据也不会丢失,会缓存在内存里。等缓存区清空后再从内存读取出来。 let flag = ws.write(chunk,[encoding],[callback]); 1. chunk写入的数据buffer/string 2. encoding编码格式chunk为字符串时有用,可选 3. callback 写入成功后的回调 > 返回值为布尔值,系统缓存区满时为false,未满时为true(缓存区不能接着写返回false,能接着写返回true)
ws.end(chunk,[encoding],[callback]); > 表明接下来没有数据要被写入 Writable 通过传入可选的 chunk 和 encoding 参数,可以在关闭流之前再写入一段数据 如果传入了可选的 callback 函数,它将作为 'finish' 事件的回调函数
// 监听可写流缓存区清空事件 // 缓存区满了后被清空了才会触发drain ws.on('drain', function () { console.log('drain'); });
ws.end('结束'); ws.on('finish', function () { console.log("写入完成"); });
参考:
1. 流的概念
2. 为什么使用流
如果读取一个文件,使用
fs.readFileSync
同步读取,程序会被阻塞,然后所有数据被写到内存中。使用fs.readFile
读取,程序不会阻塞,但是所有数据依旧会一次性全被写到内存,然后再让消费者去读取。如果文件很大,内存使用便会成为问题。 这种情况下流就比较有优势。流相比一次性写到内存中,它会先写到到一个缓冲区,然后再由消费者去读取,不用将整个文件写进内存,节省了内存空间。3. 四种流类型
Node.js中有四种基本类型的流:
4. 可读流 createReadStream
createReadStream
实现了stream.Readable
接口的对象,将对象数据读取为流数据,当监听data事件后,开始发射数据var util = require('util'); var fs = require("fs") fs.createReadStream = function(path, options) { return new ReadStream(path, options); }; util.inherits(ReadStream, Readable);
4.1 创建可读流
var rs = fs.createReadStream(path,[options]); 1.path 读取文件的路径 2.options flags打开文件要做的操作,默认为'r' encoding默认为null start开始读取的索引位置 end结束读取的索引位置(包括结束位置) highWaterMark读取缓存区默认的大小64kb > 如果指定utf8编码highWaterMark要大于3个字节
4.2 设置编码
// 与指定{encoding:'utf8'}效果相同,设置编码 rs.setEncoding('utf8');
4.3 监听data事件
// 一旦监听data事件时,流就可以读文件的内容,并且发射data。 // 根据设置的读取缓存区默认大小来决定,读一段发射一段,直到读完。 // 默认情况下,监听data事件后会不停的读数据,然后出发data事件,触发完data事件后,再次读数据。不会停。 // 希望流有一个暂停和恢复触发机制,见4.8 暂停和恢复触发data rs.on('data', function (data) { console.log(data); });
4.4 监听end事件
// 文件读完了,会触发end事件 rs.on('end', function () { console.log('读取完成'); });
4.5 监听error事件
// 文件读取出错了,会触发error事件 rs.on('error', function () { console.log("error"); });
4.6 监听open事件
// 如果是文件流还会涉及到open和close两个事件 rs.on('open', function () { console.log("文件打开"); });
4.7 监听close事件
// 如果是文件流还会涉及到open和close两个事件 rs.on('close', function () { console.log("文件关闭"); });
4.8 暂停和恢复触发data
// 通过pause()方法和resume()方法 rs.on('data', function (data) { console.log(data); rs.pause(); // 暂停读取和发射data事件 setTimeout(function () { rs.resume(); // 恢复读取并触发data事件 },2000); });
以上可以看到:
open
在data
前,open
先打开文件,然后data
读取完内容发射。end
在close
前,先发现文件读完了执行end
,然后再关闭文件close
4.9 可读流的两种模式
可读流事实上工作在下面两种模式之一:
flowing
和paused
在
flowing
模式下, 可读流自动从系统底层读取数据,并通过EventEmitter
接口的事件尽快将数据提供给应用。在
paused
模式下,必须显式调用stream.read()
方法来从流中读取数据片段。所有初始工作模式为
paused
的Readable
流,可以通过下面三种途径切换到flowing
模式:'data'
事件stream.resume()
方法stream.pipe()
方法将数据发送到Writable
可读流可以通过下面途径切换到
paused
模式:stream.pause()
方法实现。'data'
事件监听,并调用stream.unpipe()
方法移除所有管道目标来实现。5. 可写流 createWriteStream
createWriteStream
实现了stream.Writable
接口的对象将流数据写入到对象中fs.createWriteStream = function(path, options) { return new WriteStream(path, options); }; util.inherits(WriteStream, Writable);
5.1 创建可写流
// 往可写流里写数据时,不会立刻写入文件的,而会先写入缓存区, 缓存区大小就是highWaterMark,默认16k。然后等缓存区满了之后再真正的写入文件里。 var ws = fs.createWriteStream(path,[options]); 1. path写入的文件路径 2. options flags 打开文件要做的操作,默认为'w' encoding 默认为utf8 start 开始位置 highWaterMark 写入缓存区的默认大小16kb
5.2 write方法
// write方法有返回值flag,按理说返回false就不能往里面写了,但是真的写了数据也不会丢失,会缓存在内存里。等缓存区清空后再从内存读取出来。 let flag = ws.write(chunk,[encoding],[callback]); 1. chunk写入的数据buffer/string 2. encoding编码格式chunk为字符串时有用,可选 3. callback 写入成功后的回调 > 返回值为布尔值,系统缓存区满时为false,未满时为true(缓存区不能接着写返回false,能接着写返回true)
5.3 end方法
ws.end(chunk,[encoding],[callback]); > 表明接下来没有数据要被写入 Writable 通过传入可选的 chunk 和 encoding 参数,可以在关闭流之前再写入一段数据 如果传入了可选的 callback 函数,它将作为 'finish' 事件的回调函数
5.4 drain方法
// 监听可写流缓存区清空事件 // 缓存区满了后被清空了才会触发drain ws.on('drain', function () { console.log('drain'); });
5.5 finish方法
ws.end('结束'); ws.on('finish', function () { console.log("写入完成"); });
参考: