我使用protobuf-java-util:3.0.0-beta-2。
我创建了一个包含大量Protobuf消息的文件,这些消息是用Message#writeDelimitedTo()
编写的。代码是这样的:
Iterable<SomeMessage> messages = getHugeDataSet();
OutputStream os = new FileOutputStream("a_lot_of_messages.protobuf");
for (SomeMessage msg : messages) msg.writeDelimitedTo(os);
我用Builder#mergeDelimitedFrom()
看了那些文件。就像这样:
InputStream is = new FileInputStream("a_lot_of_messages.protobuf");
SomeMessage msg1 = SomeMessage.newBuilder().mergeDelimitedFrom(is).build();
SomeMessage msg2 = SomeMessage.newBuilder().mergeDelimitedFrom(is).build();
... // This is simplified - it's implemented as an Iterator in the real code
通过这种方式,我可以没有任何问题地读取绝大多数文件,但有时会遇到这样的异常:
com.google.protobuf.InvalidProtocolBufferException: Protocol message had invalid UTF-8. ...
编写这些文件的代码运行在有时会停电的移动设备上。在这种情况下,我的应用程序一直在写同一个文件,获得异常的几率很高。因此,显然我的代码在这种情况下会创建一些格式错误的文件。我的代码可以读取文件的某些部分,但由于错误,它无法读取格式错误的块之后的部分。
现在,我需要拯救和读取数据后的畸形块,但我找不到任何方法去做。因此,我想知道以下几点:
完全异常堆栈跟踪如下所示:
com.google.protobuf.InvalidProtocolBufferException: Protocol message had invalid UTF-8.
at com.google.protobuf.InvalidProtocolBufferException.invalidUtf8(InvalidProtocolBufferException.java:120) ~[protobuf-java-3.0.0-beta-2.jar:na]
at com.google.protobuf.CodedInputStream.readStringRequireUtf8(CodedInputStream.java:410) ~[protobuf-java-3.0.0-beta-2.jar:na]
at com.example.Model$SomeData.<init>(Model.java:14775) ~[my-app-1.0-SNAPSHOT.jar:na]
at com.example.Model$SomeData.<init>(Model.java:14717) ~[my-app-1.0-SNAPSHOT.jar:na]
at com.example.Model$SomeData$1.parsePartialFrom(Model.java:18240) ~[my-app-1.0-SNAPSHOT.jar:na]
at com.example.Model$SomeData$1.parsePartialFrom(Model.java:18234) ~[my-app-1.0-SNAPSHOT.jar:na]
at com.google.protobuf.CodedInputStream.readMessage(CodedInputStream.java:495) ~[protobuf-java-3.0.0-beta-2.jar:na]
at com.example.Model$SomeMessage.<init>(Model.java:27250) ~[my-app-1.0-SNAPSHOT.jar:na]
at com.example.Model$SomeMessage.<init>(Model.java:27197) ~[my-app-1.0-SNAPSHOT.jar:na]
at com.example.Model$SomeMessage$1.parsePartialFrom(Model.java:28678) ~[my-app-1.0-SNAPSHOT.jar:na]
at com.example.Model$SomeMessage$1.parsePartialFrom(Model.java:28672) ~[my-app-1.0-SNAPSHOT.jar:na]
at com.example.Model$SomeMessage$Builder.mergeFrom(Model.java:27802) ~[my-app-1.0-SNAPSHOT.jar:na]
at com.example.Model$SomeMessage$Builder.mergeFrom(Model.java:27653) ~[my-app-1.0-SNAPSHOT.jar:na]
at com.google.protobuf.AbstractMessageLite$Builder.mergeFrom(AbstractMessageLite.java:235) ~[protobuf-java-3.0.0-beta-2.jar:na]
at com.google.protobuf.AbstractMessage$Builder.mergeFrom(AbstractMessage.java:516) ~[protobuf-java-3.0.0-beta-2.jar:na]
at com.google.protobuf.AbstractMessage$Builder.mergeFrom(AbstractMessage.java:290) ~[protobuf-java-3.0.0-beta-2.jar:na]
at com.google.protobuf.AbstractMessageLite$Builder.mergeDelimitedFrom(AbstractMessageLite.java:305) ~[protobuf-java-3.0.0-beta-2.jar:na]
at com.google.protobuf.AbstractMessage$Builder.mergeDelimitedFrom(AbstractMessage.java:530) ~[protobuf-java-3.0.0-beta-2.jar:na]
at com.google.protobuf.AbstractMessageLite$Builder.mergeDelimitedFrom(AbstractMessageLite.java:311) ~[protobuf-java-3.0.0-beta-2.jar:na]
at com.google.protobuf.AbstractMessage$Builder.mergeDelimitedFrom(AbstractMessage.java:522) ~[protobuf-java-3.0.0-beta-2.jar:na]
at com.example.MyUtils.read(MyUtils.java:54) [my-app-1.0-SNAPSHOT.jar:na]
发布于 2017-11-15 06:13:16
从原始原型数据中恢复消息边界是很有问题的。这通常适用于数据格式,它越小越压缩,它对传输错误的弹性就越小。
最好的方法是在消息开始时总是出现一些字节序列。例如,如果您碰巧有任何接近常量的字段(如required string software_version = 1;
),则可以在二进制数据中搜索该字段,从而找到下一条消息的起始点。
如果不是这样的话,您可以在编写消息时引入这样的标记。例如,选择64位随机数作为标记。您可以将其单独写入protobuf之外的文件,也可以在消息中有一个索引1的fixed64
字段,以使其接近开始。
尽管标记可能随机出现在数据的其他地方,但这不应导致太大的问题,因为您可以跳过不解析的消息。
https://stackoverflow.com/questions/47256157
复制相似问题