【问题标题】:How to read PDF from the .jar file如何从 .jar 文件中读取 PDF
【发布时间】:2020-04-08 08:14:29
【问题描述】:

在我的 maven 项目中,我有位于 resources 文件夹内的 PDF 文件。我的函数从resources 文件夹中读取PDF 文件,并根据用户的数据在文档中添加一些值。

该项目使用mvn clean install 打包为.jar 文件,并在我的其他spring boot 应用程序中用作依赖项。

在我的 Spring Boot 项目中,我创建了将在 PDF 上执行一些工作的类的实例。一旦 PDF 文件上的所有工作完成,并且当 PDF 文件保存在文件系统上时,它总是空的(所有页面都是空白的)。我的印象是mvn clean install 对 PDF 文件做了一些事情。到目前为止,这是我尝试过的:

第一种方式

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
File file= new ClassPathResource("/pdfs/testpdf.pdf").getFile();//Try to get PDF file

PDDocument pdf = PDDocument.load(file);//Load PDF document from the file
List<PDField> fields = forms.getFields();//Get input fields that I want to update in the PDF    
fieldsMap.forEach(throwingConsumerWrapper((field,value) -> changeField(fields,field,value)));//Set input field values

pdf.save(byteArrayOutputStream);//Save value to the byte array

这很好用,但是一旦项目被打包到 .jar 文件中,我就会得到 new ClassPathResource("/pdfs/testpdf.pdf").getFile(); 找不到指定文件的异常。

这是正常的,因为File 类无法访问.jar 文件中的任何内容(它只能访问.jar 文件本身),这很清楚。

因此,该问题的解决方案是使用InputStream 而不是File。这是我所做的:

第二种方式

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
InputStream inputStream = new ClassPathResource("/pdfs/testpdf.pdf").getInputStream();//Try to get input stream

PDDocument pdf = PDDocument.load(inputStream );//Load PDF document from the input stream
List<PDField> fields = forms.getFields();//Get input fields that I want to update in the PDF    
fieldsMap.forEach(throwingConsumerWrapper((field,value) -> changeField(fields,field,value)));//Set input field values

pdf.save(byteArrayOutputStream);//Save value to the byte array

这次getInputStream() 不会抛出错误,并且inputStream 对象不是null。但是保存在我的文件系统上的PDF文件是空的,这意味着所有页面都是空的。

我什至尝试复制完整的 inputStream 并将其逐字节保存到文件中,但我注意到每个字节都等于 0。这是我所做的:

第三条路

InputStream inputStream = new ClassPathResource("/pdfs/test.pdf").getInputStream();
byte[] buffer = new byte[inputStream.available()];
inputStream.read(buffer);

File targetFile = new File(OUTPUT_FOLDER);
OutputStream outStream = new FileOutputStream(targetFile);
outStream.write(buffer);

复制的test.pdf 已保存,但使用 Adob​​e Reader 打开时报告为已损坏。

有人知道如何解决这个问题吗?

【问题讨论】:

  • 我不建议使用“可用”,使用InputStream.readAllBytes()

标签: java maven pdfbox


【解决方案1】:

经过几个小时的调查以及@Simon Martinelli 和@Tilman Hausherr 的良好投入,我有两个问题需要解决:

第一个问题 - 正确读取文件

为了从resources 文件夹中读取文件,您必须使用适当的类。如上所述,您不能使用File 类从.jar 读取文件,我在我的情况下使用了以下构造:

InputStream inputStream = CreatePDF.class.getResourceAsStream("/pdfs/test.pdf");
PDDocument pdf = PDDocument.load(inputStream);

在我的情况下,CreatePDF 类是静态类。如果您的类不是静态的,请使用以下内容:

InputStream inputStream = this.getClass().getResourceAsStream("/pdfs/test.pdf");
PDDocument pdf = PDDocument.load(inputStream);

第二个问题 - 我原来的问题

我在我的问题的第三个示例中注意到的一件事是,当我将文件逐字节从 resources 复制到我的本地文件夹时,所有字节都等于 0。我知道这是不正确的所以我尝试用简单的.txt 文件做同样的事情,在这种情况下一切正常。这意味着mvn clean install 在 PDF 文件上造成了一些问题。 经过一番调查,我意识到 mvn 过滤器导致了问题。如果启用了资源过滤器:

<resource>
    <directory>src/main/resources</directory>
    <filtering>true</filtering>
</resource>

那么您的二进制数据将被损坏,这是我最初的问题。当我将其设置为 false 时,它的工作方式与预期一样。

这里是来自 maven 页面的警告:

警告:不要过滤图像等二进制内容的文件!这 很可能会导致输出损坏。

如果您同时拥有文本文件和二进制文件作为资源 建议有两个单独的文件夹。一个文件夹 src/main/resources (默认) 用于未过滤的资源 和另一个文件夹 src/main/resources-filtered 的资源 被过滤。

这是一个example 你怎么做的:

<resource>
    <directory>src/main/resources</directory>
    <filtering>true</filtering>
    <includes>
        <include>**/*.properties</include>
        <include>**/*.xml</include>
        <include>**/*.txt</include>
        <include>**/*.html</include>
    </includes>
</resource>
<resource>
    <directory>src/main/resources</directory>
    <filtering>false</filtering>
    <includes>
        <include>**/*.pdf</include>
    </includes>
</resource>

【讨论】:

    【解决方案2】:

    你必须像这样加载它:

    InputStream inputStream = this.getClass().getClassloader().getResourceAsStream("/pdfs/testpdf.pdf");
    

    如果您通过 ClassLoader 加载它,则路径从类路径的根目录开始。

    【讨论】:

    • 为什么不直接this.getClass().getResourceAsStream()
    • 这将与类相关。所以不是他要找的东西
    • 我认为你应该检查一下,*.com/a/14739608/2067492 因为如果你使用“/...”那么它与类无关。此外,如果您使用类加载器,则使用“/...”是多余的