首先要知道cyber record文件的结构是什么样,网上有很多介绍的,大概就是header + n*(chunk) + indexes 这里indexes存储了topic的FileDescriptorProto,所以无需依赖额外的proto文件就能解析。 python 包用的是DescriptorPool类来存储,protobufjs没有这个类,所以用FileDescriptorSet来代替, 话不多说,我TM直接亮代码。
const arrayBuffer = fileReader.result as ArrayBuffer;
const dataView = new DataView(arrayBuffer);
const headerlength = dataView.getBigInt64(8, true);
const headerBuf = arrayBuffer.slice(16, 16 + Number(headerlength));
const header = Header.decode(new Uint8Array(headerBuf));
const indexesBuf = arrayBuffer.slice(Number(header.indexPosition) + 16);
const index = Index.decode(new Uint8Array(indexesBuf));
index.indexes.forEach((item) => {
if (item.type !== 4) {
return;
}
const fileDescriptorSet = FileDescriptorSet.decode(new Uint8Array());
const proto_desc = ProtoDesc.decode(
item.channelCache?.protoDesc ?? new Uint8Array()
);
if (proto_desc.desc.length) {
let stack = [proto_desc];
while (stack.length) {
const protpdesc = stack.shift();
const protodesc = FileDescriptorProto.decode(protpdesc.desc);
if (
!fileDescriptorSet.file.some((it) => it.name === protodesc.name)
) {
fileDescriptorSet.file.push(protodesc);
}
stack = [...stack, ...protpdesc.dependencies];
}
}
const root = protobufjs.Root.fromDescriptor(fileDescriptorSet).lookup(
item.channelCache?.messageType
);
decoderMap.set(item.channelCache?.name, root);
});
index.indexes.forEach((item) => {
if (item.type === 2) {
const position = Number(item.position);
const bodySize = dataView.getBigInt64(position + 8, true);
const bodyBuf = arrayBuffer.slice(
position + 16,
position + 16 + Number(bodySize)
);
const message = ChunkBody.decode(new Uint8Array(bodyBuf));
message.messages.forEach((msg) => {
const decoder = decoderMap.get(msg.channelName);
decoder.decode(msg.content)
})
}
});
以上代码实现是原创,要copy的记得打赏 想起了和某位领域"专家"的对话,我说用到了protobuf的反射机制,他说js没有反射机制,专家就是专家。