Open7
Node.jsでStreamを読むとき、chunkを文字列として結合してはいけない
次のBADパターンをやってはいけないということ。result
が期待どおりにならない場合があるため。
BAD
let result = ""
for await (const chunk of stream) {
result += chunk
}
GOOD
const chunks = []
for await (const chunk of stream) {
chunks.push(chunk)
}
const result = Buffer.concat(chunks).toString()
result += chunk
は、暗黙的にchunk.toString()
を呼んでいるが、chunk
がテキストとして区切りのいいバイナリデータとなっている保証がないため。
ちなみに for await (const chunk of stream) { ... }
は stream.on("data", (chunk) => { ... })
と同じ。
chunks
の型はUint8Array[]
でOK
実験してみる。
text
SUCCESS🎉
bad.js
import * as fs from "node:fs"
const stream = fs.createReadStream("./text", { highWaterMark: 4 })
let result = ""
for await (const chunk of stream) {
result += chunk
}
console.log(result)
good.js
import * as fs from "node:fs"
const stream = fs.createReadStream("./text", { highWaterMark: 4 })
const chunks = []
for await (const chunk of stream) {
chunks.push(chunk)
}
const result = Buffer.concat(chunks).toString()
console.log(result)
$ node bad.js
SUCCESS����
$ node good.js
SUCCESS🎉
text
ファイルの中身をバイナリとして見ると、絵文字部分が4 byte f0 9f 8e 89
で構成されているのがわかる。
$ od -tx1 text
0000000 53 55 43 43 45 53 53 f0 9f 8e 89
0000013
fs.createReadStream
にhighWaterMark: 4
を指定して、4 byteごとにこのファイルを区切っていくと、絵文字が絵文字の塊にならない。
53 55 43 43 SUCC
45 53 53 f0 ESS�
9f 8e 89 ���
highWaterMark: 7
にすると、bad.js
でも期待どおりの出力となるが、これはたまたま区切りがいい位置にきただけのこと。
一般性はないので注意。
53 55 43 43 45 53 53 SUCCESS
f0 9f 8e 89 🎉