nodejs开发的一个关于Buffer截断导致�字符出现的bug - pod4g/tool GitHub Wiki
一、问题的发现与排查
最近遇到一个很棘手的问题,作者在使用新CMS编辑文章时,点击保存,有很大的概率文章内的某些文字会被�
替代。
-
首先怀疑是输入法的问题,输入了某些不被识别的字符,但是我这边用跟编辑们不一样的输入法测试了一下,也是会出现这个问题,所以不大可能是输入法的问题,故排除;
-
因为CMS文章做了LocalStrorage本地缓存,所以怀疑是不是chrome的bug,经过查找资料,发现没有人遇到过这个问题,所以也不大可能是chrome的问题,故排除;
-
会不会是数据库的问题呢?经过排查,数据库编码格式完全正确,是没有问题的,打印sql语句发现,在入库之前已经有某些字符被
�
替代了。故排除数据库的问题; -
所以问题肯定出现在nodejs这一层,经过排查,发现是自己写的
bodyParser
中间件有问题
bodyParse (req, res, next) {
let data = ''
req.setEncoding('utf8')
// 取出请求数据
req.on("data", chunk => data += chunk); // eslint-disable-line
req.on('end', () => {
req.body = data
next()
})
}
这个问题在于如果数据量足够大,网络比较差,则data
事件会执行多次,如果截断的文本截断的位置正好不能被识别(即不能被utf8解码),则会出现不识别的字符被�
代替的问题。原来在我本机上进行测试从来没有发现过这个问题的原因就在于,虽然数据足够大,但是网络很好且是本机,所以data
事件只会执行一次,就没有截断问题。
二、解决办法
// 如果不获取charset也是可以的,基本上没问题,默认都是utf8。
// 但是怕万一上来的数据不是utf8编码的,那就麻烦了,所以干脆处理一下
const getCharset = req => {
let charset = ''
if (!req) return charset
let contentType = null
if (typeof req === 'string') {
contentType = req
} else {
contentType = req.headers['content-type']
}
if (!contentType) return charset
const charsetReg = /; *charset=(.+)/i
const match = contentType.match(charsetReg)
if (!match) return charset
charset = match[1]
return charset.trim().toLowerCase()
}
bodyParse (req, res, next) {
const charset = getCharset(req) || 'utf8'
// 提前设置charset会报错
// req.setEncoding(charset)
const chunks = []
let totalLength = 0
req.on('data', chunk => {
chunks.push(chunk)
totalLength += chunk.length
})
req.on('end', () => {
// 根据Content-Type的charset,来转换。Buffer的toString默认参数就是utf8
const data = Buffer.concat(chunks, totalLength).toString(charset)
req.body = data
next()
})
}
三、一些笔记
data
事件的chunk是一个Buffer实例;+=
触发了Buffer实例的toString
方法;- Buffer的
toString
方法参数默认是utf8
;
四、参考资料
- http://apps.timwhitlock.info/unicode/inspect?s=%EF%BF%BD
- https://stackoverflow.com/questions/26580265/nodejs-dealing-with-characters-encoding
- https://www.npmjs.com/package/iconv-lite
记录于:2017-07-06