因公司需求需使用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.vue
翻譯是(機翻---我英語很差)java
協議緩衝區是用於序列化結構化數據的靈活,高效的自動化機制 - 思考XML,但更小,更快,更簡單。您能夠定義一次數據的結構,而後您可使用特殊的源代碼輕鬆地將結構化數據寫入各類數據流並使用各類語言讀取和讀取數據。您甚至能夠更新您的數據結構,而不會中斷根據「舊」格式編譯的已部署程序。webpack
特色:ios
在github上寫了個demo demo地址 有須要的能夠下載下來跑一下就理解了。PS:若是以爲有用 請給我個小星星 (筆芯~)git
其實最開始我嘗試使用一個第三方JS protobuf.js protobuf.load
的時候瀏覽器報了個錯illegal token '<' (/demo.proto, line 1)查找了下官網issue,大意應該是proto文件多了個字符,可是我查看過proto文件並無發現有多的'<',怎麼辦呢,最後放棄使用第三方。用官方提供的方法。github
下載地址 (我下載的是3.40版) github也提供了zip包,可自行下載 (目前最新版本是v3.6.0) 用來編譯proto爲JS文件方便調用web
因爲公司用的是win10 只須要將下載的文件地址添加到path便可 Mac與window命令惟一的區別就是須要將protoc改爲protoc.exe 前提是須要添加環境變量 配置環境可參考這篇文章vue-cli
爲了確保先後一致,下面是後臺寫給個人一個測試proto,我司後臺是java
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
進入awesome.proto
的存放路徑 使用以下命令 protoc.exe --js_out=import_style=commonjs,binary:. awesome.proto
awesome_pb.js
文件awesome_pb.js
其實能夠看到裏面是生成好的方法。只須要在頁面中引入JS調用便可以後咱們將這個文件引入頁面,固然你也能夠考慮全局引用
編寫一個測試頁面,建立一個測試按鈕 我是在測試頁面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)
}
複製代碼
至此先後聯調完成