在java中读取二进制文件,直到特定的“%% EOF”标记?

问题描述 投票:1回答:1

我需要在java中读取二进制文件并将其拆分(它实际上是一个包含许多pdf文件的二进制文件,每行之前有一行“元数据”)。

二进制文件中的每个pdf项以"%%EOF"标记结束。

我的第一次尝试,我逐行读取文件作为UTF-8文件,但这破坏了二进制数据!

reader = new BufferedReader(new InputStreamReader(new FileInputStream(binaryFile), "UTF-8"));

String mdmeta;
while ((mdmeta = reader.readLine()) != null) {
    System.out.println("read file metadata: " + mdmeta);
    writeToFile("exploded-file-123");
}

和方法writeToFile

BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fullFilename), "UTF-8"));

writer.write("%PDF-1.4\r\n");
String line;
while ((line = reader.readLine()) != null) {
    writer.write(line);
    writer.write("\r\n");
    if ("%%EOF".equals(line)) {
        writer.flush();
        return;
    }
}

...虽然这会将文件拆分为爆炸项目,但这些二进制文件已损坏(当然因为我读取并将字节写为UTF-8字符串......)

我认为我需要一个更低级别的方法,使用InputStream。

它变得复杂,因为文件可能很大。想象一下,我使用缓冲区...我可以从文件中读取字节来填充缓冲区...然后我需要在缓冲区内查找"%%EOF" ...并手动拆分前一个爆炸项目和下一个项目之间的缓冲区。

或者如果"%%EOF"落在缓冲区边缘,那么我可能会完全错过文件边界...

我想我正在寻找某种方式来readBytesUpUntil("%%EOF") - 有一个简单的方法来做到这一点?

java file pdf stream inputstream
1个回答
3
投票

PDF查看器最后开始读取文件。他们寻找%%EOF,然后寻找xref表的起点,即交叉引用表。交叉引用表将所有对象映射到它们的字节偏移量。

例如:

  • 编号为1的对象从字节位置12578开始
  • 数字2的对象从字节位置158开始
  • 数字3的对象从字节位置9821开始
  • 编号为4的对象从字节位置18792开始
  • ...

等等。

PDF查看器还会查找/Catalog的对象编号,即PDF文档的根字典。它通过转到交叉引用表中定义的字节偏移量来搜索/Catalog对象。

从该根词典中,PDF查看器获取/Pages树的根。在/Pages树中,它获取有关PDF中页面的信息,包括在何处查找呈现页面所需的所有内容和资源。

所有这些都是通过基于对象编号从交叉引用表中检索的字节偏移量对文件进行随机访问来实现的。

现在:

  • 想象一下,你将一些任意字节插入PDF文件,
  • 想象一下,你不适应交叉引用表,
  • 您期望PDF查看器如何找到呈现文档所需的对象?

此外,PDF可以包含多个%%EOF标记。线性化PDF就是这种情况,这是逐步更新的PDF的情况。

此类PDF文件也必须从最后一个字节开始读取。在最后一个修订版的交叉引用表中,将替换一些现有对象并添加新对象,但您仍需要先前修订版的交叉引用表,否则无法呈现任何内容。

现在:

  • 想象一下,你将根据%%EOF的出现分割一个逐步更新的文件,
  • 想象一下,您将每个片段保存为单独的文件,
  • 那么只有第一个文件才是有效的PDF文件;所有连续文件都将丢失诸如字体,重用图像等资源。连续文件不是完整的PDF文档。

简而言之:

基于%%EOF的出现拆分长PDF文档是不明智的。即使将一系列有效的PDF文件粘合在一起,您也有可能最终破坏这些文件,因为单个PDF文件可能会出现多次%%EOF

© www.soinside.com 2019 - 2024. All rights reserved.