可扩展性。 大数据。 即时的。 这些是现代万维网中Web应用程序必须面对的一些挑战。 这是Node.js及其非阻塞I / O模型发挥作用的地方。 本文将向您介绍Node的功能最强大的API之一,用于数据密集型计算流 。
为什么要使用流?
让我们考虑以下示例:
var http = require('http')
, fs = require('fs')
;
var server = http.createServer(function (req, res) {
fs.readFile(__dirname + '/data.txt', function (err, data) {
res.end(data);
});
});
server.listen(8000);
此代码完美地工作。 除了Node.js的事实,这没什么不对的
在将数据发送回客户端之前,先缓冲data.txt的全部内容。 随着客户端请求的增加,您的应用程序可能开始消耗大量内存。 另外,客户端将需要等待服务器应用程序读取整个文件,从而导致延迟增加。
让我们看另一个例子:
var http = require('http')
, fs = require('fs')
;
var server = http.createServer(function (req, res) {
var stream = fs.createReadStream(__dirname + '/data.txt');
stream.pipe(res);
});
server.listen(8000);
在这里,为了克服可伸缩性问题,我们使用流API。 使用流对象可确保从磁盘读取data.txt一次将一个块发送给客户端,而无需在客户端上进行服务器缓冲和等待时间。
什么是流?
流可以定义为连续的数据流,可以在数据输入(或输出)时进行异步处理。 在Node.js中,流可以是可读或可写的。 可读流是一个EventEmitter对象,它在每次接收到一块数据时都发出data
事件。 在我们之前的示例中,使用了可读流将文件内容通过管道传输到HTTP客户端。 当流到达文件的末尾时,它将发出end
事件,指示不再发生任何data
事件。 另外,可以暂停和恢复可读流。
另一方面, 可写流接受数据流。 这种类型的流也继承自EventEmitter
对象,并实现两个方法: write()
和end()
。 第一种方法将数据写入缓冲区,如果已正确刷新数据,则返回true
如果缓冲区已满,则返回false
(在这种情况下,数据将在稍后发送出去)。end end()
方法仅指示流为完成。
您的第一流应用程序
让我们仔细看一下流。 为此,我们将构建一个简单的文件上传应用程序。 首先,我们需要构建一个客户端,该客户端使用可读流读取文件并将数据通过管道传输到特定目标。 在管道的另一端,我们将实现一个服务器,该服务器使用可写流保存上传的数据。
让我们从客户端开始。 我们从导入HTTP和文件系统模块开始。
var http = require('http')
, fs = require('fs');
然后,我们定义我们的HTTP请求。
var options = {
host: 'localhost'
, port: 8000
, path: '/'
, method: 'POST'
};
var req = http.request(options, function(res) {
console.log(res.statusCode);
});
现在我们有了request
,我们创建了一个可读流,该流读取文件并将内容通过管道传输到request
对象。
var readStream = fs.ReadStream(__dirname + "/in.txt");
readStream.pipe(req);
流完成读取所有数据后,我们将关闭与服务器的连接,并调用request
的end()
方法。
readStream.on('close', function () {
req.end();
console.log("I finished.");
});
服务器
正如我们为客户端所做的那样,我们从导入Node.js模块开始。 然后,我们创建一个新的可写流,将数据保存到文本文件中。
var http = require('http')
, fs = require('fs');
var writeStream = fs.createWriteStream(__dirname + "/out.txt");
为了让我们的客户端应用程序上传文件,我们必须创建一个新的Web服务器对象。 当数据来自request
对象时,服务器将调用我们的流并将缓冲区刷新到输出文件。
var server = http.createServer(function (req, res) {
req.on('data', function (data) {
writeStream.write(data);
});
req.on('end', function() {
writeStream.end();
res.statusCode = 200;
res.end("OK");
});
});
server.listen(8000);
请注意, createServer()
返回的req
和res
对象分别是可读流和可写流。 我们可以监听data
事件,并在所有处理结束后将结果通过管道传回客户端。
结论
本文介绍了Node.js最强大的工具之一,即流API。 在接下来的几周中,我们将更深入地研究流的世界,探索Node.js中内置的所有不同类型,以及第三方流。