最近遇见一个从前没有遇见的陷阱,就是data里的chunk拼接。
由于本人身为前端工程师,对buffer的概念实在是认识不足。这次的场景是我要通过http.get去抓取远端的网页文件,很不小心的是对方的文件编码
是gbk(估计是老年代Java环境下的解决方案),而我本地的代码是utf8的编码,最终我需要将两部分代码合并之后输出到客户端,所以我需要将接受到
的部分进行转码,转码则需要通过iconv实现。
在这之前我需要将接受到的chunk进行组装。下面是我最原始的组装方式,因为在我的概念中都把他们当做string给组装了。

var data = "";
res.on('data', function (chunk) {
data += chunk;
})
.on("end", function () {
//对data转码
});

很遗憾,我调用:

var iconv = new Iconv('GBK', 'UTF-8');
iconv.convert(data).toString();

EILSEQ异常被抛出。
其原因是两个chunk(Buffer对象)的拼接并不正常,相当于进行了buffer.toString() +
buffer.toString()。如果buffer不是完整的,则toString出来后的string是存在问题的(比如一个中文字被截断)。这样
出来的string就无法被iconv正常转码。
那么正确的拼接该是怎样呢,在大神兼好基友发烧友 的帮助指点下,以下代码才是正确的:

var chunks = [];
var size = 0;
res.on('data', function (chunk) {
chunks.push(chunk);
size += chunk.length;
});
res.on('end', function () {
var data = null;
switch(chunks.length) {
case 0: data = new Buffer(0);
break;
case 1: data = chunks[0];
break;
default:
data = new Buffer(size);
for (var i = 0, pos = 0, l = chunks.length; i < l; i++) {
var chunk = chunks[i];
chunk.copy(data, pos);
pos += chunk.length;
}
break;
}
});

这时候的data才是一个正确的buffer对象。

但是,对于接收数据而言,这样的场景应当是一个十分常见的场景才对,每次都要写这样一大堆的代码,实在是很费事的。那么我们封装重构吧:

var BufferHelper = function () {
this.buffers = [];
this.size = 0;
this._status = "changed";
}; BufferHelper.prototype.concat = function (buffer) {
for (var i = 0, l = arguments.length; i < l; i++) {
this._concat(arguments[i]);
}
return this;
}; BufferHelper.prototype._concat = function (buffer) {
this.buffers.push(buffer);
this.size = this.size + buffer.length;
this._status = "changed";
return this;
}; BufferHelper.prototype._toBuffer = function () {
var data = null;
var buffers = this.buffers;
switch(buffers.length) {
case 0:
data = new Buffer(0);
break;
case 1:
data = buffers[0];
break;
default:
data = new Buffer(this.size);
for (var i = 0, pos = 0, l = buffers.length; i < l; i++) {
var buffer = buffers[i];
buffer.copy(data, pos);
pos += buffer.length;
}
break;
}
// Cache the computed result
this._status = "computed";
this.buffer = data;
return data;
}; BufferHelper.prototype.toBuffer = function () {
return this._status === "computed" ? this.buffer : this._toBuffer();
}; BufferHelper.prototype.toString = function () {
return Buffer.prototype.toString.apply(this.toBuffer(), arguments);
}; module.exports = BufferHelper;

这里有两个私有方法,_concat和_toBuffer。其目的是保证每个方法的职责单一,还在toBuffer里做了一下状态设置,使得不浪费CPU。接下来的调用就非常之简单了。

var http = require('http');
var BufferHelper = require('bufferhelper'); http.createServer(function (request, response) {
var bufferHelper = new BufferHelper(); request.on("data", function (chunk) {
bufferHelper.concat(chunk);
}); request.on('end', function () {
var html = bufferHelper.toBuffer().toString();
response.writeHead(200);
response.end(html);
});
}).listen(8001);

可以看到代码量少了很多,跟第一种方法的使用相差无几。嗯,不虚心的自夸一下,很干净利落:)。
另外可以看到上面的代码是直接require的:

var BufferHelper = require('bufferhelper');

其原因是我已经将其发布到NPM中,可以通过npm install bufferhelper直接搞定。项目地址在github上: https://github.com/JacksonTian/bufferhelper

最后这个lib还没写单元测试,和做压测,之后会添加上。
最后谢谢基友发烧友。
最后,其实node-iconv的作者还提供了一个工具集(https://github.com/bnoordhuis/node-buffertools),是有部分通过c/c++完成的,不过我的需求没那么复杂,只要一个最简单的concat就可以满足了。相信这个bufferhelper对于中文环境下的同学是非常有用的~

小心buffer的拼接问题 --转的更多相关文章

  1. 《深入浅出Node.js》第6章 理解 Buffer

    @by Ruth92(转载请注明出处) 第6章 理解 Buffer ✁ 为什么需要 Buffer? 在 Node 中,应用需要处理网络协议.操作数据库.处理图片.接收上传文件等,在网络流和文件的操作中 ...

  2. node.js中buffer需要知道的一些点

    本文为阅读朴灵大大的<深入浅出node.js>笔记: 在前端开发的时候,我们不曾用过buffer,也没得用.buffer是node环境引入的,用来方便应对二进制数据的处理.这里我们对它应该 ...

  3. 大熊君大话NodeJS之------Buffer模块

    一,开篇分析 所谓缓冲区Buffer,就是 "临时存贮区" 的意思,是暂时存放输入输出数据的一段内存. JS语言自身只有字符串数据类型,没有二进制数据类型,因此NodeJS提供了一 ...

  4. nodejs: 理解Buffer

    学习nodejs中buffer这一章,有一段写到buffer的拼接,其中一段源码非常优美,特拿来与大家共享. var chunks = []; var size = 0; res.on('data', ...

  5. Buffer -nodejs

    纯 JavaScript 对 Unicode 友好但是无法很好地处理二进制数据.当我们面对类似 TCP 流或文件系统时,是需要处理八位流的.Node 有几种操作.创建以及消费八位流的策略.原始数据保存 ...

  6. 深入浅出Node.js (6) - 理解Buffer

    6.1 Buffer结构 6.1.1 模块结构 6.1.2 Buffer对象 6.1.3 Buffer内存分配 6.2 Buffer的转换 6.2.1 字符串转Buffer 6.2.2 Buffer转 ...

  7. NodeJS stream 一:Buffer

    当年是看了朴灵的九浅一深 NodeJS 入门的 Node, 朴大大的书讲实践很少更多的篇幅用在了讲原理上,道理听了那么多,后来开始在前端工程领域使用 NodeJS 却处处掣肘,总结原因发现 NodeJ ...

  8. nodejs中的Buffer

    一,开篇分析 所谓缓冲区Buffer,就是 "临时存贮区" 的意思,是暂时存放输入输出数据的一段内存. JS语言自身只有字符串数据类型,没有二进制数据类型,因此NodeJS提供了一 ...

  9. golang字符串拼接

    四种拼接方案: 1,直接用 += 操作符, 直接将多个字符串拼接. 最直观的方法, 不过当数据量非常大时用这种拼接访求是非常低效的. 2,直接用 + 操作符,这个和+=其实一个意思了. 3,用字符串切 ...

随机推荐

  1. CUBRID学习笔记 16 元数据支持

    简化了很多 ,在sqlserver需要用语句实现的功能 接口如下 public DataTable GetDatabases(string[] filters) public DataTable Ge ...

  2. python_way ,json(自学)

    python_way ,json 如果我们想将多行字典存放到文件中,并且还需要调出这些字典继续使用那么就要是用json. 首先将字典用json转换成字符串,存放到文件中. a = {"tel ...

  3. STL--list

    List-概述: 列表List是一个线性链表结构(Double—Linked Lists,双链表),它的数据由若干个节点构成,每一个节点都包括一个信息块Info(即实际存储的数据).一个前驱指针Pre ...

  4. SG函数题目

    HDU Fibonacci again and again 思路: 把整个游戏看成三个子游戏,然后求游戏的和 关键理解g(x) = mex(g(y), y€f(x)) , f(x)表示由x点可达的点, ...

  5. JZs3c2440裸板程序GPIO操作总结

    分别用汇编,汇编+C两种方式 ***************************************汇编编程led_on.s********************************** ...

  6. Mysql 允许null 与 default值

    分为下面4种情况: 1.允许null, 指定default值. 2.允许null, 不指定default,这个时候可认为default值就是null 3.不允许null,指定default值,不能指定 ...

  7. SVM(支持向量机)与统计机器学习 & 也说一下KNN算法

    因为SVM和统计机器学习内容很多,所以从 http://www.cnblogs.com/charlesblc/p/6188562.html 这篇文章里面分出来,单独写. 为什么说SVM和统计学关系很大 ...

  8. 关于协程的学习 & 线程栈默认10M

    先看的这篇文章:http://blog.csdn.net/qq910894904/article/details/41699541 以nginx为代表的事件驱动的异步server正在横扫天下,那么事件 ...

  9. Android 热补丁动态修复框架小结

    一.概述 最新github上开源了很多热补丁动态修复框架,大致有: https://github.com/dodola/HotFix https://github.com/jasonross/Nuwa ...

  10. 关于图片加载非常爽的一个三方控件 fresco,一个三fresco

    Hi  EveryBody 今天来玩一个非常爽的控件 fresco 到底有多爽呢 接着看就知道了 首先 来看看fresco 是个神马东西 https://github.com/facebook/fre ...