流
我们在 HTTP 中看过两个可写流的例子,即服务器可以向response
对象中写入数据,而request
返回的请求对象也可以写入数据。
可写流是 Node 中广泛使用的概念。这种对象拥有write
方法,你可以传递字符串或Buffer
对象,来向流写入一些数据。它们end
方法用于关闭流,并且还可以接受一个可选值,在流关闭之前将其写入流。 这两个方法也可以接受回调作为附加参数,当写入或关闭完成时它们将被调用。
我们也可以使用fs
模块的createWriteStream
,建立一个指向本地文件的输出流。你可以调用该方法返回的结果对象的write
方法,每次向文件中写入一段数据,而不是像writeFile
那样一次性写入所有数据。
可读流则略为复杂。传递给 HTTP 服务器回调的request
绑定,以及传递给 HTTP 客户端回调的response
对象都是可读流(服务器读取请求并写入响应,而客户端则先写入请求,然后读取响应)。读取流需要使用事件处理器,而不是方法。
Node 中发出的事件都有一个on
方法,类似浏览器中的addEventListener
方法。该方法接受一个事件名和一个函数,并将函数注册到事件上,接下来每当指定事件发生时,都会调用注册的函数。
可读流有data
事件和end
事件。data
事件在每次数据到来时触发,end
事件在流结束时触发。该模型适用于“流”数据,这类数据可以立即处理,即使整个文档的数据没有到位。我们可以使用createReadStream
函数创建一个可读流,来读取本地文件。
这段代码创建了一个服务器并读取请求正文,然后将读取到的数据全部转换成大写,并使用流写回客户端。
const {createServer} = require("http");
createServer((request, response) => {
response.writeHead(200, {"Content-Type": "text/plain"});
request.on("data", chunk =>
response.write(chunk.toString().toUpperCase()));
request.on("end", () => response.end());
});
}).listen(8000);
传递给data
处理器的chunk
值是一个二进制Buffer
对象,我们可以使用它的toString
方法,通过将其解码为 UTF-8 编码的字符,来将其转换为字符串。
下面的一段代码,和上面的服务(将字母转换成大写)一起运行时,它会向服务器发送一个请求并输出获取到的响应数据:
const {request} = require("http");
request({
hostname: "localhost",
port: 8000,
method: "POST"
}, response => {
response.on("data", chunk =>
process.stdout.write(chunk.toString()));
}).end("Hello server");
// → HELLO SERVER
该示例代码向process.stdout
(进程的标准输出流,是一个可写流)中写入数据,而不使用console.log
,因为console.log
函数会在输出的每段文本后加上额外的换行符,在这里不太合适。