Protocol Buffers(簡稱:ProtoBuf)是一種開源跨平台的序列化資料結構的協定。其對於儲存資料或在網絡上進行通訊的程式是很有用的。這個方法包含一個介面描述語言,描述一些資料結構,並提供程式工具根據這些描述產生程式碼,這些代碼將用來生成或解析代表這些數據結構的位元組流。

ProtoBuf类似于json xml是一种网络数据交换的新格式。以下例子使用nodejs作为后端,原生html+js作为前端,使用protobufjs这个纯js库处理ProtoBuf。其中后端使用proto文件作为消息类,前端使用json对象作为消息类。

后端 Link to heading

const express = require('express')
const protobuf = require('protobufjs')
const path = require('path')

const app = express()
const port = 3000
const htmlPath = path.join(__dirname, './index.html')
const protoPath = path.join(__dirname, './awesome.proto')

const payload = {
  name: 'lihua',
  age: 28,
  address: '上海市浦东新区纳贤路701号'
}

app.get('/', (req, res) => {
  res.sendFile(htmlPath)
})

app.get('/api', async (req, res) => {
  const root = await protobuf.load(protoPath)
  const AwesomeMessage = root.lookupType("awesomepackage.AwesomeMessage")
  const errMsg = AwesomeMessage.verify(payload);
  if (errMsg) throw Error(errMsg);
  const message = AwesomeMessage.create(payload);
  const buffer = AwesomeMessage.encode(message).finish();
  res.setHeader('content-type', 'application/x-protobuf')
  res.send(buffer);
})

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})

awesome.proto

package awesomepackage;
syntax = "proto3";

message AwesomeMessage {
  string name = 1;
  int32 age = 2;
  string address = 3;
}

前端 Link to heading

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://cdn.jsdelivr.net/npm/protobufjs@7.2.3/dist/protobuf.min.js"></script>
  <style>
    span{
      display: inline-block;
      padding: 0px 8px;
    }
  </style>
</head>
<body>
  <div>
    <span id="name">name:</span>
    <span id="age">age:</span>
    <span id="address">address:</span>
  </div>
  <script>
    const proto = {
      "nested": {
        "awesomepackage": {
          "nested": {
            "AwesomeMessage": {
              "fields": {
                "name": {
                  "type": "string",
                  "id": 1
                },
                "age": {
                  "type": "int32",
                  "id": 2
                },
                "address": {
                  "type": "string",
                  "id": 3
                }
              }
            }
          }
        }
      }
    }
    fetch('/api')
      .then(res => res.arrayBuffer())
      .then(buffer => {
        const root = protobuf.Root.fromJSON(proto);
        const AwesomeMessage = root.lookupType("awesomepackage.AwesomeMessage")
        const message = AwesomeMessage.decode(new Uint8Array(buffer))
        const name = document.querySelector('#name')
        const age = document.querySelector('#age')
        const address = document.querySelector('#address')
        name.innerHTML = `name: ${message.name}`
        age.innerHTML = `age: ${message.age}`
        address.innerHTML = `address: ${message.address}`
      })
  </script>
</body>
</html>