You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java 8 Fat-JAR构建后无法读取JPEG2000图片的解决咨询

问题

我需要构建面向非开发者的、包含所有依赖的Java 8可执行JAR,核心功能是通过JavaFX的ImageView组件展示PDF内容,部分PDF内嵌JPEG2000格式图片,因此在pom.xml中添加了如下依赖:

<dependency>
    <groupId>com.github.jai-imageio</groupId>
    <artifactId>jai-imageio-core</artifactId>
    <version>1.4.0</version>
</dependency>
<dependency>
    <groupId>com.github.jai-imageio</groupId>
    <artifactId>jai-imageio-jpeg2000</artifactId>
    <version>1.3.0</version>
</dependency>
<dependency>
    <groupId>org.apache.pdfbox</groupId>
    <artifactId>jbig2-imageio</artifactId>
    <version>3.0.0</version>
</dependency>

本地.m2仓库中的类结构如附图所示,在IntelliJ 2024.2.4社区版中运行程序时,所有图片(含JPEG2000)均可正常显示。

但通过IntelliJ执行pom.xml的clean和package命令生成Fat-JAR后,发现JAR内com.github.jai-imageio包的路径与本地.m2不同。运行该可执行JAR并加载含JPEG2000图片的PDF时,抛出如下错误:

11/12/2024-09:10:42 ERROR  PDFStreamEngine(935) - Cannot read JPEG2000 image: Java Advanced Imaging (JAI) Image I/O Tools are not installed
org.apache.pdfbox.filter.MissingImageReaderException: Cannot read JPEG2000 image: Java Advanced Imaging (JAI) Image I/O Tools are not installed
    at org.apache.pdfbox.filter.Filter.findImageReader(Filter.java:172) ~[CoverSheetCreatorFX_new.jar:?]
    at org.apache.pdfbox.filter.JPXFilter.readJPX(JPXFilter.java:123) ~[CoverSheetCreatorFX_new.jar:?]
    at org.apache.pdfbox.filter.JPXFilter.decode(JPXFilter.java:72) ~[CoverSheetCreatorFX_new.jar:?]
    at org.apache.pdfbox.filter.Filter.decode(Filter.java:254) ~[CoverSheetCreatorFX_new.jar:?]
    at org.apache.pdfbox.cos.COSInputStream.create(COSInputStream.java:73) ~[CoverSheetCreatorFX_new.jar:?]
    at org.apache.pdfbox.cos.COSStream.createInputStream(COSStream.java:172) ~[CoverSheetCreatorFX_new.jar:?]
    at org.apache.pdfbox.pdmodel.common.PDStream.createInputStream(PDStream.java:193) ~[CoverSheetCreatorFX_new.jar:?]
    at org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject.createInputStream(PDImageXObject.java:895) ~[CoverSheetCreatorFX_new.jar:?]
    at org.apache.pdfbox.pdmodel.graphics.image.SampledImageReader.from8bit(SampledImageReader.java:469) ~[CoverSheetCreatorFX_new.jar:?]
    at org.apache.pdfbox.pdmodel.graphics.image.SampledImageReader.getRGBImage(SampledImageReader.java:217) ~[CoverSheetCreatorFX_new.jar:?]
    at org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject.getImage(PDImageXObject.java:477) ~[CoverSheetCreatorFX_new.jar:?]
    at org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject.getImage(PDImageXObject.java:438) ~[CoverSheetCreatorFX_new.jar:?]
    at org.apache.pdfbox.rendering.PageDrawer.drawImage(PageDrawer.java:1248) ~[CoverSheetCreatorFX_new.jar:?]
    at org.apache.pdfbox.contentstream.operator.graphics.DrawObject.process(DrawObject.java:74) ~[CoverSheetCreatorFX_new.jar:?]
    at org.apache.pdfbox.contentstream.PDFStreamEngine.processOperator(PDFStreamEngine.java:893) ~[CoverSheetCreatorFX_new.jar:?]
    at org.apache.pdfbox.contentstream.PDFStreamEngine.processStreamOperators(PDFStreamEngine.java:531) ~[CoverSheetCreatorFX_new.jar:?]
    at org.apache.pdfbox.contentstream.PDFStreamEngine.processStream(PDFStreamEngine.java:506) ~[CoverSheetCreatorFX_new.jar:?]
    at org.apache.pdfbox.contentstream.PDFStreamEngine.processPage(PDFStreamEngine.java:153) ~[CoverSheetCreatorFX_new.jar:?]
    at org.apache.pdfbox.rendering.PageDrawer.drawPage(PageDrawer.java:286) ~[CoverSheetCreatorFX_new.jar:?]
    at org.apache.pdfbox.rendering.PDFRenderer.renderImage(PDFRenderer.java:330) ~[CoverSheetCreatorFX_new.jar:?]
    at org.apache.pdfbox.rendering.PDFRenderer.renderImage(PDFRenderer.java:247) ~[CoverSheetCreatorFX_new.jar:?]
    at org.apache.pdfbox.rendering.PDFRenderer.renderImageWithDPI(PDFRenderer.java:219) ~[CoverSheetCreatorFX_new.jar:?]

当前开发环境为Java 8,pom.xml的build配置如下:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                    <configuration>
                        <archive>
                            <manifest>
                                <mainClass>CoverSheetCreatorFX</mainClass>
                            </manifest>
                        </archive>
                        <descriptorRefs>
                            <descriptorRef>jar-with-dependencies</descriptorRef>
                        </descriptorRefs>
                        <appendAssemblyId>false</appendAssemblyId>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

我推测pom.xml构建过程导致路径异常是问题根源,若该推测正确,如何修改pom.xml或其他配置解决问题?若推测错误,下一步该如何排查?

解决方案

一、路径异常推测的验证与修复

你的推测大概率正确,maven-assembly-plugin打包fat jar时,仅简单合并类文件,容易丢失JAI ImageIO依赖的SPI配置文件(META-INF/services/javax.imageio.spi.ImageReaderSpi),或导致文件冲突,进而让PDFBox无法找到JPEG2000的解析器。

修改方案1:替换为maven-shade-plugin打包

maven-shade-plugin能更好处理SPI配置合并、依赖冲突等问题,是打包fat jar的更优选择:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.4.1</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <transformers>
                            <!-- 合并所有META-INF/services下的SPI配置文件 -->
                            <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                            <!-- 指定可执行JAR的主类 -->
                            <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                <mainClass>CoverSheetCreatorFX</mainClass>
                            </transformer>
                        </transformers>
                        <!-- 排除签名文件,避免打包时因重复签名报错 -->
                        <filters>
                            <filter>
                                <artifact>*:*</artifact>
                                <excludes>
                                    <exclude>META-INF/*.SF</exclude>
                                    <exclude>META-INF/*.DSA</exclude>
                                    <exclude>META-INF/*.RSA</exclude>
                                </excludes>
                            </filter>
                        </filters>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

替换后执行mvn clean package,生成的fat jar会正确合并SPI配置,确保PDFBox能找到JPEG2000的ImageReader。

修改方案2:自定义assembly描述符(不推荐,较繁琐)

若坚持使用maven-assembly-plugin,需自定义描述符保留SPI配置:

  1. 在项目根目录创建src/main/assembly/assembly.xml:
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
    <id>jar-with-dependencies</id>
    <formats>
        <format>jar</format>
    </formats>
    <includeBaseDirectory>false</includeBaseDirectory>
    <dependencySets>
        <dependencySet>
            <outputDirectory>/</outputDirectory>
            <useProjectArtifact>true</useProjectArtifact>
            <unpack>true</unpack>
            <scope>runtime</scope>
        </dependencySet>
    </dependencySets>
    <fileSets>
        <fileSet>
            <directory>${project.build.outputDirectory}</directory>
            <outputDirectory>/</outputDirectory>
        </fileSet>
    </fileSets>
    <containerDescriptorHandlers>
        <!-- 合并META-INF/services下的配置文件 -->
        <containerDescriptorHandler implementation="org.apache.maven.plugins.assembly.containerdescriptorhandler.ServicesHandler"/>
    </containerDescriptorHandlers>
</assembly>
  1. 修改pom.xml中的assembly-plugin配置,指定自定义描述符:
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>single</goal>
            </goals>
            <configuration>
                <archive>
                    <manifest>
                        <mainClass>CoverSheetCreatorFX</mainClass>
                    </manifest>
                </archive>
                <descriptors>
                    <descriptor>src/main/assembly/assembly.xml</descriptor>
                </descriptors>
                <appendAssemblyId>false</appendAssemblyId>
            </configuration>
        </execution>
    </executions>
</plugin>

二、若推测错误的排查步骤

如果修改打包插件后问题依旧,按以下步骤排查:

  • 检查fat jar文件结构:解压生成的JAR,确认com/github/jai-imageio路径下的类是否存在,同时查看META-INF/services/javax.imageio.spi.ImageReaderSpi文件中是否包含com.github.jaiimageio.impl.plugins.jpeg2000.JPEG2000ImageReaderSpi条目。
  • 手动验证SPI加载:在项目中添加调试代码,打印所有可用的ImageReader,确认JPEG2000解析器是否被加载:
    import javax.imageio.spi.IIORegistry;
    import javax.imageio.spi.ImageReaderSpi;
    import java.util.Iterator;
    
    public class DebugSPI {
        public static void printImageReaders() {
            IIORegistry registry = IIORegistry.getDefaultInstance();
            Iterator<ImageReaderSpi> readers = registry.getServiceProviders(ImageReaderSpi.class, false);
            while (readers.hasNext()) {
                ImageReaderSpi spi = readers.next();
                System.out.println("Loaded ImageReader: " + spi.getPluginClassName());
            }
        }
    }
    
    调用该方法后运行fat jar,查看输出是否包含JPEG2000相关的Reader类。
  • 排查依赖冲突:执行mvn dependency:tree,查看是否有其他依赖覆盖了jai-imageio的类,或存在版本冲突。
  • 验证Java 8兼容性:尝试更换jai-imageio的版本(比如jai-imageio-core:1.3.1),确认版本与Java 8完全兼容。

内容的提问来源于stack exchange,提问作者N C

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 10:25:01