Node.js Stream 详解之编码篇
经过流中转的数据,可能会经历编码转换。 本文介绍可读流和可写流中一些常见的编码转化情况。
假定 options 为创建流时传给 Readable 或 Writable 的配置。 正常情况下,流只处理 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类型则称为解码,实际是将二进制根据指定编码翻译成字符串。
如果编码和解码指定的编码方式不一样,就实现了一次编码转换。 譬如上面的YWJj到abc便是一次从Base-64到
UTF-8的编码转换。 这样的现象,也会出现在流操作数据的过程中。
可读流
外界与可读流的数据沟通是通过push(data, encoding)和on('data', chunk => {})来实现的。 本小节研究data, chunk, encoding,options.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))
输出:
abcdef
由于options.encoding设置为'utf8',所以,'YMJj'与'ZGVm'的二进制表示均按照UTF-8的编码方式进行解码再输出。
可见,encoding控制对输入进行编码,而options.encoding控制对输出进行解码。 如果encoding等于options.encoding,这两步其实都不会发生,也没必要发生。
chunk实际是data编码转换后的结果。
对象模式下接受任意类型的输入
如果设置options.objectMode为true,则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))
输出:
YMJjZGVm
[object Object]
试图对String和Buffer以外的数据类型进行解码(调用toString()),其结果通常不是所预期的。
如果不设置options.encoding,则结果将为:
YMJj<Buffer 5a 47 56 6d>
{}
可写流
外界与可写流的数据沟通是通过write(data, encoding)和_write(chunk, enc, next)来实现的。 本小节研究data, chunk, encoding,enc,options.objectMode,以及options.decodeStrings之间的关系。
与可读流相比,没有了options.encoding,意味着chunk不再是解码的结果。
readable.push与writable.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'
以上是 Node.js Stream 详解之编码篇 的全部内容, 来源链接: utcz.com/z/264107.html
