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))
输出:
abc
def
由于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))
输出:
YMJj
ZGVm
[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'
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论