Nodejs中关于Buffer的binary格式问题
Nodejs中关于Buffer的binary格式问题
这格式到底什么意思?比方说console.log(new Buffer(‘牛’)); 输出<Buffer,e7,89,9b> e7,89,9b是utf-8没错. 然后 var buf0=new Buffer(‘牛’); console.log(new Buffer(buf0.toString(‘binary’))); 输出<Buffer,c3,a7,c2,89,c2,9b> 这转换算法怎么来的?我追踪了下V8代码,无果,某地转换成binary似乎是把字符全部转到int16_t宽度然后又拷回去,但这样不应该得出这样的结果啊.
在 Node.js 中,Buffer 对象用于处理二进制数据,它是 Node.js 中非常重要的一个特性。对于 Buffer 的 binary 格式问题,我们可以深入了解一下。
首先,让我们理解一下 binary 格式。binary 是一种编码方式,它将每个字符视为一个字节(8位),因此可以表示 256 种不同的值。这种编码方式常用于处理原始二进制数据,例如图像或音频文件。
在您的例子中,new Buffer('牛') 创建了一个包含三个字节的缓冲区,因为 “牛” 在 UTF-8 编码中占用 3 个字节(e7, 89, 9b)。
接下来,当我们使用 buf0.toString('binary') 将 Buffer 转换为 binary 字符串时,实际上会将每个字节视为一个独立的字节,并将其转换为对应的 ASCII 码。
让我们看一个具体的例子来更好地理解这一点:
// 创建一个包含中文字符 '牛' 的 Buffer
var buf0 = new Buffer('牛');
// 打印原始 Buffer
console.log(buf0); // 输出: <Buffer e7 89 9b>
// 将 Buffer 转换为 binary 字符串
var binaryString = buf0.toString('binary');
console.log(binaryString); // 输出: '\xe7\x89\x9b'
// 将 binary 字符串转换回 Buffer
var buf1 = new Buffer(binaryString, 'binary');
console.log(buf1); // 输出: <Buffer e7 89 9b>
在这个例子中,我们创建了一个包含 “牛” 的 Buffer。当我们使用 toString('binary') 方法将这个 Buffer 转换为 binary 字符串时,每个字节都被转换为对应的 ASCII 码。这些 ASCII 码被打印出来后,看起来像是 c3 a7 c2 89 c2 9b,这是因为 binary 编码实际上是将每个字节视为独立的字节,并将其转换为相应的 ASCII 码。
最后,我们将这个 binary 字符串再次转换回 Buffer,可以看到结果与原始 Buffer 相同。
希望这个解释和示例代码能帮助您更好地理解 Node.js 中 Buffer 的 binary 格式问题。
完整的Buffer构造函数是new Buffer(str, [encoding]), encoding的默认值是utf-8。
console.log(new Buffer(buf0.toString('binary')));
等于
console.log(new Buffer(buf0.toString('binary'),'utf-8'));
这里第二个初始化参数encoding不能用默认的utf-8,而应该用 ?(你猜)。
我把工程告诉你们好了:enter link description here
file.js里面 让人下载文件的代码:
var filename = allpath.replace(/^.*[\\\/]/, '');
var buf0=new Buffer(filename);
res.download(allpath,buf0.toString('binary'));
return;
这里filename 本身就是utf-8的,但我非得用binary参数否则中文的文件名乱码.我想知道原因,因为 binary据说要废弃,没替代方案岂不傻眼
你这种下载是错误且低效的
自己看API实现吧,不要迷信这些仅仅实现了功能的框架
现在我认为主要是Buffer问题,用api实现也要附加上文件名啊. 和express的实现里set(‘Content-Disposition’, ‘attachment; filename="’+filename+’"'会有什么不同?不就是文件名数据不同嘛.
如此,实际你想问的其实与本帖的主题关系不大,虽然你认为是Buffer出问题了,不过呢这只是表象,导致中文乱码的元凶是http模块发送header的方式。 先从结论而言:nodejs仅支持单字节字符(准确的说是ascii)的header,所有的多字节字符都会被去掉高位字节,只保留最低位字符。
filename='中文名.txt';// \u4e2d\u6587\u540d.txt;js内部字符编码是unicode
res.download(allpath,filename);
客户端得到的文件名就变成:\x2d\x87\x0d.txt,高位被去掉了。 testcase: https://gist.github.com/shiedman/5472925
进一步挖掘源码看看究竟怎么回事,从response.write这个方法开始追踪,最终定位到OutgoingMessage.prototype._send(https://github.com/joyent/node/blob/v0.10.5-release/lib/http.js#L488)
this.output.unshift(this._header);
this.outputEncodings.unshift('ascii');
实际的写入操作发生在OutgoingMessage.prototype._writeRaw(https://github.com/joyent/node/blob/v0.10.5-release/lib/http.js#L505)
var c = this.output.shift();
var e = this.outputEncodings.shift();
this.connection.write(c, e);
一入队一出队,相互对应,header的发送相当于固定为connection.write(header,'ascii'),当header包含中文字符,高字节被削,然后乱码;当header包含binary编码的字符,与ascii同属单字节,字节流得以完整保留,文件名显示正常。(PS:response.end的实现与response.write稍有出入,在特定的情况下可指定header的编码,有兴趣的同学可翻下源码)
至于如何输出正确的中文文件名,提供2个思路:
1.拦截_writeRaw的调用,将this._header的对应outputEncoding设为utf-8后,恢复执行流程
var _fn=res._writeRaw;
res._writeRaw=function(){
res.output.forEach(function(e,i){
if(e==res._headers){res.outputEncodings[i]=='utf-8';}
})
_fn.apply(res,arguments);
}
(大概如此,未实际验证)
2.遵循RFC规范 详细的讨论见http://stackoverflow.com/questions/93551/how-to-encode-the-filename-parameter-of-content-disposition-header-in-http
var userAgent=(request.headers['user-agent']||'').toLowerCase();
if(userAgent.indexOf('msie')>=0 || userAgent.indexOf('chrome')>=0){
headers['Content-Disposition']='attachment; filename='+encodeURIComponent(filename);
}else if(userAgent.indexOf('firefox')>=0){
headers['Content-Disposition']='attachment; filename*="utf8\'\''+encodeURIComponent(filename)+'"';
} else{
/** safari等其他非主流浏览器只能自求多福了 **/
headers['Content-Disposition']='attachment; filename='+new Buffer(filename).toString('binary');
}
除开某个畸形的IE版本和特殊的浏览器,大部分情况下都能正确显示中文名。 PS:心中虽明白,写出来却花了大半小时,郁闷。
谢了,不过这些解决方案一点不优雅,还是继续binary好了…这应该算nodejs的bug,也许后面会矫正.
唉正如你所说下载都是二进制,和什么编码没有关系的,BUFFER这个相对低效的元素最好不要出现在常用函数中 看了半天不知道你说的是什么乱码,是需要读取的文件名是中文还是什么 如果NODE不支持二进制字符串,自己也是可以实现的,不需要用BUFFER来转 / 不过还是看不明白LZ具体是什么问题
因为BINARY STRING就是“乱码”,对UNICODE友好的JS不合适处理这种东西,NODE才要把它去除
在Node.js中,Buffer对象用于处理二进制数据。当你使用new Buffer('牛')创建一个Buffer时,默认情况下它会根据指定的字符串编码(如UTF-8)来存储字符。
例如:
console.log(new Buffer('牛'));
// 输出: <Buffer e7 89 9b>
这里,'牛'被编码为UTF-8,因此得到的是三个字节e7 89 9b。
接下来,当你尝试将这个Buffer转换为二进制格式并重新生成一个新的Buffer时,会发生一些不同的事情。buf.toString('binary')实际上是将Buffer中的每个字节视为一个8位无符号整数,并将其转换为对应的ASCII字符。这些ASCII字符在字符串中以字节的形式表示。
例如:
var buf0 = new Buffer('牛');
console.log(new Buffer(buf0.toString('binary')));
// 输出: <Buffer c3 a7 c2 89 c2 9b>
这里,buf0.toString('binary')实际上返回了一个包含原始字节的字符串,每个字节作为一个字符存在。当这个字符串再次转换为Buffer时,这些字符会被作为ASCII字符存储,因此得到的是c3 a7 c2 89 c2 9b。
简而言之,toString('binary')方法将Buffer的每个字节视为一个独立的字节值,并将其转换为相应的ASCII字符。这些字符在新的Buffer中以字节形式存储,因此出现了你看到的结果。
如果你希望保持原始的UTF-8编码,可以直接使用原始Buffer,而不需要转换为binary格式。

