| ```
1
2
``` | <td class="code">
<div class="top-box hide">
</div>
```
var builder = ProtoBuf.loadProtoFile("./example.proto");
var Message = builder.build(“Message”);
</td>
</tr>
</table></figure>
对于声明了 package 的`.proto`,只需在构建时把包名带上就行。<figure class="highlight javascript">
<table>
<tr>
<td class="gutter">
```
<span class="line">1</span>
<span class="line">2</span>
</td>
<td class="code">
<div class="top-box hide">
</div>
```
var builder = ProtoBuf.loadProtoFile("./example.proto");
var Message = builder.build(“com.xxx.Message”);
</td>
</tr>
</table></figure>
使用`loadProtoFile()`会让`.proto`文件明文暴露,所以可以使用 ProtoBuf.js 提供的工具将`.proto`转义成 `json` 或 `js`,参见:[https://github.com/dcodeIO/ProtoBuf.js/wiki/pbjs](https://github.com/dcodeIO/ProtoBuf.js/wiki/pbjs)
安装 node 模块:<figure class="highlight bash">
<table>
<tr>
<td class="gutter">
```
<span class="line">1</span>
</td>
<td class="code">
```
npm install -g protobufjs
</td>
</tr>
</table></figure>
以 [example.proto](https://github.com/dcodeIO/ProtoBuf.js/blob/master/examples/websocket/www/example.proto) 为例,在终端执行:<figure class="highlight bash">
<table>
<tr>
<td class="gutter">
```
<span class="line">1</span>
</td>
<td class="code">
```
pbjs src/address_book.proto -t js
</td>
</tr>
</table></figure>
会输出:<figure class="highlight javascript">
<table>
<tr>
<td class="gutter">
<div class="top-box hide">
</div>
```
<span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
</td>
<td class="code">
<div class="top-box hide">
</div>
```
var _root = dcodeIO.ProtoBuf.newBuilder({})[‘import’]({
“package”: null,
“messages”: [
{
“name”: “Message”,
“fields”: [
{
“rule”: “required”,
“type”: “string”,
“name”: “text”,
“id”: 1
}
]
}
]
}).build();
</td>
</tr>
</table></figure>
在实际应用时,通常一个 `.proto` 文件里面会有很多个 Message 类型,所以会将输出结果保存为一个 builder,<figure class="highlight javascript">
<table>
<tr>
<td class="gutter">
<div class="top-box hide">
</div>
```
<span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
<span class="line">13</span>
<span class="line">14</span>
<span class="line">15</span>
<span class="line">16</span>
</td>
<td class="code">
<div class="top-box hide">
</div>
```
var builder = dcodeIO.ProtoBuf.newBuilder({})[‘import’]({
“package”: null,
“messages”: [
{
“name”: “Message”,
“fields”: [
{
“rule”: “required”,
“type”: “string”,
“name”: “text”,
“id”: 1
}
]
}
]
});
</td>
</tr>
</table></figure>
存成 builder 就可以根据需求构建 Message 类型对象,<figure class="highlight javascript">
<table>
<tr>
<td class="gutter">
```
<span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
</td>
<td class="code">
<div class="top-box hide">
</div>
```
var Message = builder.build(“Message”);
var msg = new Message({
text: ‘message from maxzhang.’
});
</td>
</tr>
</table></figure>
## 序列化和反序列化 {#序列化和反序列化}
在 Web 端使用 Protocol Buffers 时,无论发送还是接收的数据都应当是二进制格式,二进制数据可以直接使用 Message 类型对象解析,<figure class="highlight javascript">
<table>
<tr>
<td class="gutter">
```
<span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
<span class="line">7</span>
<span class="line">8</span>
<span class="line">9</span>
<span class="line">10</span>
<span class="line">11</span>
<span class="line">12</span>
</td>
<td class="code">
<div class="top-box hide">
</div>
```
function encode(jsonData) {
var Message = builder.build(“Message”);
var msg = new Message(jsonData);
return msg.toArrayBuffer();
}
function decodeMessage(data) {
var msg = builder.build(“Message”).decode(data);
return msg;
}
</td>
</tr>
</table></figure>
`decode()` 返回一个 Message 实例对象(可以等同于 JSON Object),实例中的属性便是 `.proto` 文件中声明的变量与类型,
在 `.proto` 文件中声明数据类型需要遵循 [Protocol Buffers 数据类型](https://developers.google.com/protocol-buffers/docs/proto3#scalar) 规则,如下表:

由于上图不包括 JavaScript 对应的数据类型,所以我自己补充了一个数据类型对应关系(每种数据类型我并没有一一验证使用过,可能有误,欢迎指正):
| .proto Type | JavaScript Type |
| ----------- | --------------- |
| double | Long |
| float | float |
| int32 | int |
| int64 | Long |
| uint32 | int |
| uint64 | Long |
| sint32 | int |
| sint64 | Long |
| fixed32 | int |
| fixed64 | Long |
| sfixed32 | int |
| sfixed64 | Long |
| bool | boolean |
| string | string |
| bytes | ByteBuffer |
### ByteBuffer.js {#ByteBuffer-js}
bytes 类型是二进制格式数据,需要使用 [ByteBuffer.js](https://github.com/dcodeIO/ByteBuffer.js) 处理,ByteBuffer 可以直接操作二进制数据,例子:<figure class="highlight javascript">
<table>
<tr>
<td class="gutter">
```
<span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
</td>
<td class="code">
<div class="top-box hide">
</div>
```
var ByteBuffer = require(“bytebuffer”);
var bb = new ByteBuffer()
.writeIString(“Hello world!")
.flip();
console.log(bb.readIString() + ” from ByteBuffer.js");
</td>
</tr>
</table></figure>
ByteBuffer 可以直接写入或读取任意一种类型的值,值得长度为 8bits – 64bits,特殊的按位写入需要使用 JavaScript 位移操作符,比如:<figure class="highlight javascript">
<table>
<tr>
<td class="gutter">
```
<span class="line">1</span>
<span class="line">2</span>
<span class="line">3</span>
<span class="line">4</span>
<span class="line">5</span>
<span class="line">6</span>
</td>
<td class="code">
<div class="top-box hide">
</div>
```
var bb = new ByteBuffer(16);
var id = 1;
bb.writeInt8(String(id).length << 4);
bb.writeInt8(id);
bb.flip();
</td>
</tr>
</table></figure>
更多 ByteBuffer 接口参见API:[https://github.com/dcodeIO/ByteBuffer.js/wiki/API](https://github.com/dcodeIO/ByteBuffer.js/wiki/API)
在 [Message API](https://htmlpreview.github.io/?https://raw.githubusercontent.com/dcodeIO/ProtoBuf.js/master/docs/ProtoBuf.Builder.Message.html) 中的 `toArrayBuffer()` `toBuffer()` 等方法底层实际调用的是 ByteBuffer 的接口,与 ByteBuffer 不同的是“Message 对象是按照 JSON 的方式修改值,调用 `toArrayBuffer()` 接口序列化数据,调用 `decode()` 接口反序列数据”。
### Long.js {#Long-js}
由于 JavaScript 精度问题,所以 `int64` 和 `uint64` 等类型数据会被转换成 [Long.js](https://github.com/dcodeIO/Long.js) 对象实例,Long 并不太复杂,与 [bignumber.js](https://github.com/MikeMcl/bignumber.js) 类似,具体参考 [Long.js API](https://github.com/dcodeIO/Long.js#api).
## WebSocket {#WebSocket}
关于 WebSocket 提供一个简单的[例子](https://github.com/dcodeIO/ProtoBuf.js/blob/master/examples/websocket/www/index.html),
实际应用与例子差不多,就是做两件基础的事:
* 连接 WebSocket,从 socket 通道拿到二进制数据,反序列化解析成 Message 对象。
* 实例化 Message 对象,然后序列化成二进制数据,发送给服务端。
## 参考文章 {#参考文章}
* [http://www.ibm.com/developerworks/cn/linux/l-cn-gpb/](http://www.ibm.com/developerworks/cn/linux/l-cn-gpb/)
* [http://www.cnblogs.com/royenhome/archive/2010/10/29/1864860.html](http://www.cnblogs.com/royenhome/archive/2010/10/29/1864860.html)
转自:[http://www.maxzhang.com/2015/09/ProtoBuf-js使用技巧/](http://www.maxzhang.com/2015/09/ProtoBuf-js%E4%BD%BF%E7%94%A8%E6%8A%80%E5%B7%A7/)
💬 评论