因公司需求需使用protobuf,而后就有了这个踩坑记录
官方解释为:javascript
Protocol buffers are a flexible, efficient, automated mechanism for serializing structured data – think XML, but smaller, faster, and simpler. You define how you want your data to be structured once, then you can use special generated source code to easily write and read your structured data to and from a variety of data streams and using a variety of languages. You can even update your data structure without breaking deployed programs that are compiled against the "old" format.
翻译是(机翻---我英语很差)前端
协议缓冲区是用于序列化结构化数据的灵活,高效的自动化机制 - 思考XML,但更小,更快,更简单。您能够定义一次数据的结构,而后您能够使用特殊的源代码轻松地将结构化数据写入各类数据流并使用各类语言读取和读取数据。您甚至能够更新您的数据结构,而不会中断根据“旧”格式编译的已部署程序。
特色:vue
在github上写了个demo
demo地址 有须要的能够下载下来跑一下就理解了。PS:若是以为有用 请给我个小星星 (笔芯~)
其实最开始我尝试使用一个第三方JS protobuf.jsprotobuf.load
的时候浏览器报了个错illegal token '<' (/demo.proto, line 1)查找了下官网issue,大意应该是proto文件多了个字符,可是我查看过proto文件并无发现有多的'<',怎么办呢,最后放弃使用第三方。用官方提供的方法。java
下载地址 (我下载的是3.40版)
github也提供了zip包,可自行下载 (目前最新版本是v3.6.0)
用来编译proto为JS文件方便调用webpack
因为公司用的是win10 只须要将下载的文件地址添加到path便可
Mac与window命令惟一的区别就是须要将protoc改为protoc.exe 前提是须要添加环境变量
配置环境可参考这篇文章ios
为了确保先后一致,下面是后台写给个人一个测试proto,我司后台是javagit
syntax = "proto2";//protobuf版本 option java_package = "com.test.protobuf"; option java_outer_classname = "PersonMessage"; message Person { required int32 id = 1; optional string name = 2; optional string email = 3; repeated string list = 4; extensions 100 to 1000;//容许扩展的ID } message PersonTree { optional string id = 1; optional string title = 2; repeated PersonTree childs = 3; } extend Person { optional int32 count = 101; optional int32 likes_cnt= 102; } message PersonEx { optional int32 id = 1; extend Person { optional int32 px = 103; optional int32 py= 104; } optional Person p = 2; }
npm install -g vue-cli vue init webpack my-project cd my-project npm install npm run dev
安装插件:npm install axios element-ui google-protobuf --save
github
进入awesome.proto
的存放路径 使用以下命令protoc.exe --js_out=import_style=commonjs,binary:. awesome.proto
web
awesome_pb.js
文件awesome_pb.js
其实能够看到里面是生成好的方法。只须要在页面中引入JS调用便可以后咱们将这个文件引入页面,固然你也能够考虑全局引用vue-cli
编写一个测试页面,建立一个测试按钮
我是在测试页面import messages from './awesome_pb.js'
方法为:
methods: { protobufferTest () { var message = new messages.Person() // 调用Person对象 实例化 // 赋值 message.setId(23) message.setName('asd') // 序列化 var bytes = message.serializeBinary() console.log(bytes) // Uint8Array(7) [8, 23, 18, 3, 97, 115, 100] // 反序列化 var message2 = messages.Person.deserializeBinary(bytes) console.log(message2) // proto.PersonTree {wrappers_: null, messageId_: undefined, arrayIndexOffset_: -1, array: Array(3), pivot_: 1.7976931348623157e+308, …} } }
到此,本地测试完成,没什么毛病了。
前方有坑
先后传输是使用的FormData,而后悲剧的事情来了。后台解析不了。查看了下数据 [8, 23, 18, 3, 97, 115, 100]
确实是传过去了。 后来排查出缘由是应该是解析成了字符串,而后数值变了。因此解析不出来。
后来使用fromCharCode()方法编辑成字符串形式传输给后台。在使用charCodeAt()取值。
此方法已弃用 protobufferTest () { var message = new messages.Person() // 调用Person对象 实例化 // 赋值 message.setId(23) message.setName('asd') // 序列化 var bytes = message.serializeBinary() console.log(bytes) // Uint8Array(7) [8, 23, 18, 3, 97, 115, 100] var tests = '' for (let index = 0; index < bytes.length; index++) { tests += String.fromCharCode(bytes[index]) } console.log(tests) // asd // 存入FormData let uploadDatas = new FormData() uploadDatas.append('protobuf', tests) // 使用axios传输给后台 this.axios.post('/test', uploadDatas) .then(function (response) { // 将传回的字符串转为数组 console.log(response.data.split('')) // ["↵", "", "3", "2", "", "", "a", "s", "d", "f"] let str = response.data.split('') let toChar = [] for (let index = 0; index < str.length; index++) { toChar.push(str[index].charCodeAt()) } console.log(toChar) // [10, 2, 51, 50, 18, 4, 97, 115, 100, 102] // 后台传回来的是PersonTree里面的值因此调用PersonTree来反序列化 var message2 = messages.PersonTree.deserializeBinary(toChar) console.log(message2) // proto.PersonTree {wrappers_: null, messageId_: undefined, arrayIndexOffset_: -1, array: Array(3), pivot_: 1.7976931348623157e+308, …} // 获取PersonTree的id值 console.log(message2.getId()) // 32 }) .catch(function (error) { console.log(error) }) }
以上方法可能存在安全隐患。
向后端传值
由于FormData支持两种方式传输string和blob因此将bytes存入blob中
前端获取数据
对axios的默认传输方式作个更改
axios.defaults.responseType = 'arraybuffer'
将以上的JS代码更改成如下内容
protobufferTest () { var message = new messages.Person() message.setId(23) message.setName('asd') var bytes = message.serializeBinary() console.log(bytes) let uploadDatas = new FormData() var blob = new Blob([bytes], {type: 'application/octet-stream'}) uploadDatas.append('protobuf', blob) this.axios.post('/test', uploadDatas) .then(function (response) { console.log(response) var message2 = messages.PersonTree.deserializeBinary(response.data) console.log(message2.getId()) }) .catch(function (error) { console.log(error) }) // console.log(bytes) }
至此先后联调完成