商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > 如何在 Java 打包为 JAR 后正确加载资源文件(如图片)

如何在 Java 打包为 JAR 后正确加载资源文件(如图片)

  发布于2026-07-06 阅读(0)

扫一扫,手机访问

在 IntelliJ 里能正常加载的图片资源,打包成 JAR 后就因为 getResource() 返回 null 而翻车。说白了,根本原因就一个:IDE 的类路径和构建工具(比如 Ma ven)对资源文件的处理机制不一样——某些目录你没明确声明,它就不会被打进 JAR,运行时自然找不到。

在 IntelliJ 里能正常加载的图片资源,打包成 JAR 后就因为 getResource() 返回 null 而翻车。说白了,根本原因就一个:IDE 的类路径和构建工具(比如 Ma ven)对资源文件的处理机制不一样——某些目录你没明确声明,它就不会被打进 JAR,运行时自然找不到。

做 Ja va 桌面或游戏开发(比如用 Jaylib)时,通过 ClassLoader.getResource() 去加载图片、音频这类资源,算是常规操作。但有个坑几乎人人都踩过:代码在 IntelliJ IDEA 里运行得顺顺当当,一旦打包成 JAR,就给你抛出一个 NullPointerException 或 URISyntaxException。从堆栈日志来看,异常点一般都卡在 ZipFileSystemProvider.getPath() 这一步,这已经非常明确地指向一件事——目标资源根本就没被塞进最终的 JAR 包里。

根本原因在哪

IntelliJ 默认会把 src/main/resources 或者你手动标记的任何目录,都加到运行时的类路径里。所以你在 IDE 里调 getClass().getClassLoader().getResource("assets/img/hero.png") 总能成功。但 Ma ven 只认 src/main/resources 这个默认目录。如果你的图片放在 src/main/assets/、resources/images/ 或者其他自定义路径下,又没有在 pom.xml 里显式声明,那这些文件就不会出现在生成的 JAR 的 classpath 里。getResource() 返回 null,也就一点都不奇怪了。

正确的解决方式:显式配置 Ma ven 资源目录

办法很简单,在 pom.xml 的 节点里,加上 配置,把你想打包的资源目录都声明清楚:

                src/main/resources                  src/main/assets                              src/main/resources/images                    org.apache.ma ven.plugins      ma ven-shade-plugin      3.5.0                        package                      shade                                                              com.github.command17.mariomaker17.Main                                                        

验证一下有没有生效:跑一遍 mvn clean package,然后用 jar -tf target/your-app.jar | grep "your-image.png" 看一眼,确认目标文件确实在 JAR 包里。

更健壮的资源加载方式

别再用 Path.of(...) 去直接解析 JAR 内部的 URI 了,这玩意儿在 ZIP 文件系统里特别容易出问题。改用流式读取,稳定得多:

@Nullablepublic InputStream getInputStream() {    URL resource = getClass().getClassLoader().getResource(this.location);    if (resource == null) {        LOGGER.error("Resource not found: {}", this.location);        return null;    }    try {        return resource.openStream(); // 这种方式对 JAR 和文件系统都安全    } catch (IOException e) {        LOGGER.error("Failed to open resource stream: {}", this.location, e);        return null;    }}// 举个例子(Jaylib 场景)public Texture2D loadTexture(String imagePath) {    try (InputStream is = getInputStream()) {        if (is == null) throw new RuntimeException("Texture resource missing: " + imagePath);        // 假设 Jaylib 有从 InputStream 创建纹理的方法        return LoadTextureFromImage(LoadImageFromMemory(is));    } catch (Exception e) {        throw new RuntimeException("Failed to load texture: " + imagePath, e);    }}

需要注意的几个点

  • 路径分隔符一定要用正斜杠 /:"assets/sprites/player.png" 是没问题的,"assets\sprites\player.png" 在 Windows 反斜杠下就翻车了,资源路径不认这个;
  • 别用 File 构造器去操作 JAR 内部的资源:new File(url.toURI()) 在 JAR 环境里会直接抛 FileSystemNotFoundException;
  • 注意 Shadow JAR 和 Fat JAR 的区别:如果用的 Gradle Shadow plugin,得确认 mergeServiceFiles() 有没有不小心覆盖了关键服务配置(比如图像解码器 SPI);
  • 别忽略 IDE 的缓存问题:改完 pom.xml 后,最好在 IDEA 里点一下 File → Reload project,不然新配置可能根本没生效。

把资源目录声明清楚,再加上健壮的流式加载逻辑,那个“IDE 能跑、JAR 报错”的经典问题就能彻底解决。以后在任何部署环境下,行为都是一致的。

本文转载于:https://www.php.cn/faq/2442181.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注