重要声明:本文章仅仅代表了作者个人对此观点的理解和表述。读者请查阅时持自己的意见进行讨论。
一、问题重现
在使用nodejs的http模块时,如果请求的页面不是UTF8的编码,那么响应中的中文就会乱码。比如下面的代码就会导致乱码:
let http = require("http");
http.get("http://www.people.com.cn",response => {
response.setEncoding("UTF-8");
let html = "";
response.on("data", chunk => {
html += chunk;
});
response.on("end", () => {
console.log(html);
});
});
运行这段代码,会得到中文乱码。因为人民网(www.people.com.cn)返回的页面编码是GBK的编码。而上述代码是将编码设为了: UTF-8.
当你以为兴高采烈把UTF-8改成GBK就万事大吉的话,那就真的too yang too simple了。不出意外,直接修改编码再重新运行,直接就会得到下面的错误:
string_decoder.js:34
throw new Error(`Unknown encoding: ${enc}`);
^
Error: Unknown encoding: GBK
at normalizeEncoding (string_decoder.js:34:11)
at new StringDecoder (string_decoder.js:43:19)
....
是的,没错。这压根儿就不认你这个编码格式。
二、寻找原因并解决
经过Object.getPrototypeOf(response)
的结果发现。response 原来是IncomingMessage
。而IncomingMessage
是继承了ReadableStream
的,继续向上追溯会找到readable
接口。我们知道readable
定义了这个setEncoding
方法,所以这里response的setEncoding
方法其实只支持一部分编码格式:ASCII、UTF-8、UTF-16LE/UCS-2、Base64、Binary、Hex。很遗憾,这里并没有我们期望的GBK
什么的...。
那么能不能我们自己来将Buffer转换为对应格式呢?当然是可以的。而且有现成的工具可以完成。
iconv-lite组件可以实现往更多种编码上进行转换。它可以帮我们实现buffer向字符串进行多种编码的转换。支持的编码如下:
utf8, ucs2/utf16-le, ascii, binary, base64, hex,
utf16, utf16-be, utf-7, utf-7-imap, utf32, utf32-le, utf32-be...等等
查看更多支持的编码详情,请点击这里。
现在,我们知道了node默认不支持GBK了。只需要得到原来的buffer数据,进行自己转换就好了。
let http = require("http");
let iconv = require("iconv-lite");
http.get("http://www.people.com.cn",response => {
// 因为现在自己解码,所以就不设置编码了。
// response.setEncoding("GBK");
// buffer数据现在使用数组来保存
let buffer = [];
let bufferLength = 0;
response.on("data", chunk => {
buffer.push(chunk);
bufferLength += chunk.length;
});
response.on("end", () => {
// 现在将所有buffer组合为一个整体buffer
let bufferData = Buffer.concat(buffer, bufferLength);
// 使用 iconv 以 GBK 编码转换为字符串。
var html = iconv.decode(bufferData, "GBK");
console.log(html); // 现在得到的页面内容就没有乱码的了。
});
});