背景以及protobuf.js不在多交代。直接上使用方法和注意事项。

1、安装

npm i -S protobufjs

2、根据后台提供的.proto文件生成js

后台提供的.proto文件大概是如下这样的:

package PBServer;

message hello {
    optional bytes name = 1;

    .....
}

.......

主体结构就是上边这样。

在src下新建一个文件夹proto,把这个.proto文件放进去。

在项目根目录下执行下边脚本:

"scripts": {
    "dev": "vite --https --mode dev",
    "build": "vite build --mode prod",

    /***** 在package.json文件中放置 proto 脚本  ********/
    "proto": "pbjs -t json-module -w commonjs -o src/proto/proto.js src/proto/*.proto",
    "preview":

直接执行 npm run proto。即可在proto文件夹下自动生成一个proto.js的文件。如果以后后台给你的proto文件变化了,直接运行 npm run proto即可重新生成新的proto.js。proto.js这个文件在后边帮助我们将proto翻译为json。

3、接收数据 解码数据

接受后台的数据,通常情况下是二进制数据,也就是blob。如下:

 那么我现在要做的就是将二进制数据转为json数据。

实际上protobufjs内置了一些方法。解析方法如下:

// 第一步 通过lookup函数找到要解析的包名和字段名
// 如上,我们上边的.proto文件中包名是 PBserver,字段名是 hello。
import protobuf from 'protobufjs'
import protoRoot from '@/proto/proto.js'

// 找到要翻译的包和字段
const pb = protobuf.lookup('PBserver.hello');

// 然后使用内置方法将数据转为ArraryBuffer
const buf = protoRoot.util.newBuffer(data);

// 解码 decode内置方法

return pb.decode(buf)

以上是使用内置方法处理二进制数据。

但是

我在vue3项目中使用的时候,出现了问题。所有解析出来的数据全部为空!!!

经过一步一步分析之后,发现是二进制转buffer这一步出现问题,转完buffer之后,数据全部为空。所以怀疑是转buffer这里的方法出了什么问题。

既然这样,就自己去写转buffer的方法,亲测有效,代码如下:

......


const fileReader = new FileReader();
const proto = protoRoot.lookupType('PBserver.hello');
fileReader.onload = function () {
     const data = this.result;
     const u8 = new Uint8Array(data as ArrayBuffer);
     const msg = proto.decode(u8);           
     // 最后这个 msg 就是我们成功解析的数据
};


......

4、最后注意

如果你使用的vuecli创建的项目,那么这条注意就不用看了,因为大概率不会出现问题。

如果使用的是vite构建的项目,那么在proto.js中会出现报错:

require is undefined

因为我们生成protojs的时候使用的commonjs,所以包引入的时候全部使用的 require 和 module.exports。但是vite打包构建是基于es module(import export)的。所以报错了。

这个只要将出现 require 的地方改为 import from,module.exports 的地方改为 export (default) 即可。

________________更新_____________________________

5、异步解析

请注意,fileReader.onload 这个函数是异步解析的,所以操作的时候要注意,否则数据会接受不全。

比如现在,这些数据全部是异步后台请求的PB数据,需要分别解析每个数据,然后再push进数组,那么这时候请使用 promise。

代码如下:

// 注意 这是我写的一个闭包 目的是不让 dataTemp 过早释放,导致数据不能接受完全
const receiveMsg = (function () {
        var dataTemp: any = [];
        return function (e: any) {
            if (typeof e.data != 'string') {
                dataTemp.push(e.data);
            } else {
                // 9003 代表后台数据传输完毕
                if (e.data.indexOf('9003') > -1) {
                    // 在这里使用promise -》transformData 函数如下
                    const final = dataTemp.map(async (item: any) => {
                        return await transformData(item);
                    });    
                    // 要注意  这个 final 最后是一个 promise的数组  需要全部执行 得到结果
                    // 所以如下使用all方法
                    Promise.all(final).then((res) => {
                        worker1.postMessage(res);
                        // 清除闭包占有空间
                        dataTemp = null;
                    });
                }
            }
        };
    })();


// transformData 函数使用promise处理接受数据
import protoRoot from '@/proto/proto.js';
export const transformData = function (item: any) {
    return new Promise((resolve, reject) => {
        const fileReader = new FileReader();
        const proto = protoRoot.lookupType('DMSDataFile.DataFileItem');
        fileReader.readAsArrayBuffer(item);
        fileReader.onload = function () {
            const data: any = this.result;
            const u8 = new Uint8Array(data as ArrayBuffer);
            resolve(proto.decode(u8));
        };
    });
};

6、其他转化的方法 比如 unit8Array 转化为 string

这里直接提供一个比较完整的版本,转换的时候不会出现中文乱码

// unit8array 转 string
function Uint8ArrayToString(array) {
    var out, i, len, c;
    var char2, char3;

    out = '';
    len = array.length;
    i = 0;
    while (i < len) {
        c = array[i++];
        switch (c >> 4) {
            case 0:
            case 1:
            case 2:
            case 3:
            case 4:
            case 5:
            case 6:
            case 7:
                // 0xxxxxxx
                out += String.fromCharCode(c);
                break;
            case 12:
            case 13:
                // 110x xxxx   10xx xxxx
                char2 = array[i++];
                out += String.fromCharCode(((c & 0x1f) << 6) | (char2 & 0x3f));
                break;
            case 14:
                // 1110 xxxx  10xx xxxx  10xx xxxx
                char2 = array[i++];
                char3 = array[i++];
                out += String.fromCharCode(
                    ((c & 0x0f) << 12) | ((char2 & 0x3f) << 6) | ((char3 & 0x3f) << 0)
                );
                break;
        }
    }

    return out;
}

Logo

前往低代码交流专区

更多推荐