Javascript 基于 protobufjs 接入 protobuf
1. Google Protocol Buffer
Google Protocol Buffer(简称 Protobuf)是 Google 公司内部的混合语言数据标准。Protobuf 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前以支持十多种主流编程语言。
官方技术文档 https://developers.google.com/protocol-buffers
2. 安装 protobufjs
protobufjs GitHub仓库,前端直接使用 npm 安装即可。
npm install protobufjs
3. 编写 .proto 文件
// 举例:user_login.proto
syntax = "proto3";
package user;
message UserInfoRequest {
int64 userId = 1;
}
message UserInfoResponse {
int32 userType = 1;
string mobile = 2;
}
4. protobufjs 加载 .proto 文件
// 从protobufjs中引入加载器
import { load } from 'protobufjs';
// 加载user_login.proto并解析
load('user_login.proto', (err, root) => {
if (err) {
return;
}
// Obtain a message type
const UserInfoRequest = root.lookupType('user.UserInfoRequest');
// Exemplary payload
const payload = {
userId: 1
}
// Verify the payload if necessary (i.e. when possibly incomplete or invalid)
const errMsg = UserInfoRequest.verify(payload);
if (errMsg) {
console.error(errMsg);
return;
}
// Create a new message
var message = UserInfoRequest.create(payload); // or use .fromObject if conversion is necessary
// Encode a message to an Uint8Array (browser) or Buffer (node)
var messageBuffer = UserInfoRequest.encode(message).finish();
// do something with messageBuffer
})
5. 消息发送
这时候如果将上文解析得到的 messageBuffer 直接通过 http 请求发送出去,服务端会报下面这个错误。这说明数据在服务器侧解析的过程中出了些问题,格式没对应上。
com.google.protobuf.InvalidProtocolBufferException: Protocol message contained an invalid tag (zero);
仔细观察上述代码,可以发现 UserInfoRequest.encode 之后拿到的二进制数据格式是 Uint8Array,但是 protobuf 使用的二进制数据格式是 ArrayBuffer,二者并没有对应上。于是,我们需要在发送前将 Uint8Array 转换为 ArrayBuffer。具体的代码如下:
axios.create({
url: '/hello',
method: 'post',
headers: {
'Content-Type': "application/x-protobuf"
},
responseType: 'arraybuffer',
// https://stackoverflow.com/questions/37228285/uint8array-to-arraybuffer
data: messageBuffer.buffer.slice(
messageBuffer.byteOffset,
messageBuffer.byteOffset + messageBuffer.byteLength
)
})
6. 消息接收
同样的,服务器返回的编码格式是 ArrayBuffer,需要先转化为 Uint8Array,再使用 protobufjs 解析,对应的报文格式定义在 UserInfoResponse 。
// axios返回内容
const res = response.data;
load('user_login.proto', (err, root) => {
const UserInfoResponse = root.lookupType('user.UserInfoResponse');
var message = UserInfoResponse.decode(new Uint8Array(res));
var object = UserInfoResponse.toObject(message, {
longs: String,
enums: String,
bytes: String,
// see ConversionOptions
});
console.log(object);
// 输出形如 { userType: 1234, mobile: 'xxx' }
})