Node.js Stream 详解之编码篇

发布于 2021-12-18 18:23:23 字数 6762 浏览 997 评论 0

经过流中转的数据,可能会经历编码转换。 本文介绍可读流和可写流中一些常见的编码转化情况。

假定 options 为创建流时传给 ReadableWritable 的配置。 正常情况下,流只处理 String 和 Buffer 类型的数据,但可以设置options.objectMode,使流能处理任意类型的数据。 此时,称流处于对象模式(object mode)。

下面是一个编码转化的例子:

var buf = new Buffer('YWJj', 'base64')

console.log('Binary:', buf)
// Binary: <Buffer 61 62 63>

console.log('Base-64:', buf.toString('base64'))
// Base-64: YWJj

console.log('UTF-8:', buf.toString('utf8'))
// UTF-8: abc

数据从String类型转化成Buffer类型称为编码,实际是得到字符串在指定编码方式下的二进制表示。

Buffer类型转化成String类型则称为解码,实际是将二进制根据指定编码翻译成字符串。

如果编码和解码指定的编码方式不一样,就实现了一次编码转换。 譬如上面的YWJjabc便是一次从Base-64UTF-8的编码转换。 这样的现象,也会出现在流操作数据的过程中。

可读流

外界与可读流的数据沟通是通过push(data, encoding)on('data', chunk => {})来实现的。 本小节研究data, chunk, encodingoptions.encoding,以及options.objectMode之间的关系。

对输入进行编码

push方法未指定encoding时,默认使用options.defaultEncoding。后者的默认值是'utf8'。 所以,可以认为encoding一定有值。

在非对象模式下,添加到流中的文本会进行一次编码。

const stream = require('stream')

const source = ['YWJj', 'ZGVm']
const readable = stream.Readable({
  read: function () {
    const data = source.length ? source.shift() : null
    this.push(data, 'base64')
  }
})

readable.on('data', data => console.log(data))

输出:

<Buffer 61 62 63>
<Buffer 64 65 66>

在上面的例子中,encoding 值为 'base64'。 所以 'YMJj' 与 'ZGVm' 以 Base-64 的编码方式表示,被输出。

对输出进行解码

在前小节的例子中,可以通过设置options.encoding来对输出进行解码:

const stream = require('stream')

const source = ['YWJj', 'ZGVm']
const readable = stream.Readable({
  encoding: 'utf8',
  read: function () {
    const data = source.length ? source.shift() : null
    this.push(data, 'base64')
  }
})

readable.on('data', data => console.log(data))

输出:

abc
def

由于options.encoding设置为'utf8',所以,'YMJj''ZGVm'的二进制表示均按照UTF-8的编码方式进行解码再输出。

可见,encoding控制对输入进行编码,而options.encoding控制对输出进行解码。 如果encoding等于options.encoding,这两步其实都不会发生,也没必要发生。

chunk实际是data编码转换后的结果。

对象模式下接受任意类型的输入

如果设置options.objectModetrue,则data可以是任意类型,流不再对输入进行编码。

但是如果指定了options.encoding,且push方法未指定encoding,则输出前仍然会进行解码。

const stream = require('stream')

const source = ['YMJj', Buffer('ZGVm'), {}]
const readable = stream.Readable({
  objectMode: true,
  encoding: 'utf8',
  read: function () {
    const data = source.length ? source.shift() : null
    this.push(data)
  }
})

readable.on('data', chunk => console.log(chunk))

输出:

YMJj
ZGVm
[object Object]

试图对StringBuffer以外的数据类型进行解码(调用toString()),其结果通常不是所预期的。

如果不设置options.encoding,则结果将为:

YMJj
<Buffer 5a 47 56 6d>
{}

可写流

外界与可写流的数据沟通是通过write(data, encoding)_write(chunk, enc, next)来实现的。 本小节研究data, chunk, encodingencoptions.objectMode,以及options.decodeStrings之间的关系。

与可读流相比,没有了options.encoding,意味着chunk不再是解码的结果。

readable.pushwritable.write都是往流中添加数据,push方法会使数据经历编码和解码两个步骤再输出,但write只有编码这一个环节。 事实上Writable不能设置options.encoding

所以,如果不是对象模式,chunk一定是Buffer对象,_write中的enc值一定是buffer

const stream = require('stream')

const source = ['YWJj', 'ZGVm']
const writable = stream.Writable({
  write: function (chunk, enc, next) {
    console.log(chunk, enc)
    next()
  }
})

writable.write(source[0], 'base64')
writable.write(source[1], 'base64')
writable.end()

输出:

<Buffer 61 62 63> 'buffer'
<Buffer 64 65 66> 'buffer'

enc表示对data进行何种转化得到chunk

对象模式:

const stream = require('stream')

const source = ['abc', Buffer('def')]
const writable = stream.Writable({
  objectMode: true,
  write: function (chunk, enc, next) {
    console.log(chunk, enc)
    next()
  }
})

writable.write(source[0])
writable.write(source[1])
writable.end()

输出:

abc utf8
<Buffer 64 65 66> 'buffer'

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

0 文章
0 评论
519 人气
更多

推荐作者

花开柳相依

文章 0 评论 0

zyhello

文章 0 评论 0

故友

文章 0 评论 0

对风讲故事

文章 0 评论 0

Oo萌小芽oO

文章 0 评论 0

梦明

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文