第 46 章 buffer

Buffer 类是用来处理二进制数据,因为常用,所以直接放在全局变量,使用时无需 require

46.1 常用属性和方法

46.1.1 Buffer.from(string [, encoding])

字符串转为 Buffer。

46.1.2 buf.toString([encoding[, start[, end]]])

将 Buffer 转为字符串。

46.1.3 buf.length

buf.length:返回 内存为此 Buffer 实例所申请的字节数,并不是 Buffer 实例内容的字节数

46.2 stream

Stream 是 Node.js 提供的处理流数据的抽象接口。Node.js 中有许多对象是 stream 对象,例如 request。在 Unix 系统中流就是一个很常见也很重要的概念,从术语上讲流是对输入输出设备的抽象。

46.2.1 为什么要使用流

举个例子,如果直接读取一个非常大的文件(如视频文件),一次性读取则需要占用大量内存,这对于运行在服务器的程序来说是不可取的。因此就有了流,将大文件分段读取,每次读入以少部分进行处理,这样就可以在有限的服务器资源中并发处理一定规模的请求。Node.js 设计好了 stream 流,我们只需调用其接口,不用关心底层如何实现。

46.2.2 Node.js 中的流类别

Node.js 内建了四类流, Readable 流、Writable 流、Duplex 流和 Transform 流的基类。另外如果觉得上述四类基类流不能满足需求,可以编写自己的扩充类流。

流是有方向的数据,按照流动方向可以分为三种流:

  1. 设备流向程序:readable
  2. 程序流向设备:writable
  3. 双向:duplex、transform

当一个流同时面向生产者和消费者服务的时候我们会选择 Duplex,当只是对数据做一些转换工作的时候我们便会选择使用 Tranform。

NodeJS 关于流的操作被封装到了 Stream 模块,这个模块也被多个核心模块所引用。按照 Unix 的哲学:一切皆文件,在 NodeJS 中对文件的处理多数使用流来完成:

  1. 普通文件
  2. 设备文件(stdin、stdout)
  3. 网络文件(http、net)

在 NodeJS 中所有的 Stream 都是 EventEmitter 的实例。

46.2.3 读取文件流

fs.createReadStream 方法可以创建读取文件信息的可读文件流。可读流是生产数据用来供程序消费的流。

let fileReadStream = fs.createReadStream('data.json')

fileReadStream.on('data', (chuck) => {
    console.log(`接收到数据:${chuck.length}`)
})

46.2.4 可读流的事件

可读流的常用事件有:

  1. data 当数据传入的时候就会触发;
  2. error 当读取数据流时发生错误,就会触发;
  3. end 数据全部读取完毕后触发。

46.2.5 写入流信息

先创建合适的可写流,再使用 write 方法写入到文件:

let fileReadStream = fs.createReadStream('data.json')
let fileWriteStream = fs.createWriteStream('data-2.json')

fileReadStream.on('data', (chuck) => {
    console.log(`接收到数据:${chuck.length}`)
    fileWriteStream.write(chuck)
})

46.2.6 管道 pipe

pipe 管道,可以在写入流之前,进行中间环节的处理,如压缩、加密、改变编码方式等等。多个管道可以对接。

46.3 HTPP 模块

Node.js 的 HTTP API 是非常底层的。 它只涉及流处理与消息解析。 它把一个消息解析成消息头和消息主体,但不解析具体的消息头或消息主体。

46.3.1 http.ServerResponse 类

Response 类可以发送请求,接收请求。示例如下:

const http = require('http')

var options = {
    protocol: 'http:',
    hostname: 'api.douban.com',
    port: '80',
    method: 'GET',
    path: '/v2/movie/top250'
}
var responseData = ''
var request = http.request(options, (response) => {
    console.log(response.statusCode)
    console.log(response.headers)
    // response.setEncoding = 'utf8'
    response.on('data', (chuck) => {
        // console.log(chuck)
        responseData += chuck
    })
    response.on('end', () => {
        JSON.parse(responseData).subjects.map((item) => {
            console.log(item.title)
        })
    })
})

request.on('error', (error) => {
    console.log(error)
})

request.end()

http.request()返回一个http.ClientRequest类的实例,该实例是一个可写的信息流。

46.3.2 创建服务器

使用createServer()方法可以创建 www 服务器,示例如下:

'use strict'

const http = require('http')

var server = http.createServer()

server.on('request', (request, response) => {
    response.writeHead(200, {
        'Content-Type': 'text/html'
    })
    response.end(`<h1>hello :) </h1>`)
})

server.listen(8080)

其中server中的request事件发生于用户请求服务时。可以通过server.listen()方法指定 www 服务器运行端口。