【问题标题】:Can not read spring boot packaging jar file无法读取spring boot打包jar文件
【发布时间】:2019-05-31 21:41:15
【问题描述】:

环境:Ubuntu 18 + Spring boot 2.0 + JDK 11

应用在IntelliJ中运行时可以正常运行,但是在Spring boot maven插件打包文件后读取文件时就不行了。

PS:确实可以在打包的jar文件中找到该文件!

java.io.FileNotFoundException: class path resource [jmxremote.password] 
cannot be resolved to absolute file path because it does not reside in the 
file system: jar:file:/home/XXX/YYY/target/YYY-1.0-Final.jar!/BOOT-
INF/classes!/jmxremote.password

【问题讨论】:

    标签: java spring-boot ubuntu


    【解决方案1】:

    您很可能尝试使用java.io 类来加载类路径资源。这不起作用,java.io 类仅用于文件系统操作。

    在 JAR 之外提取 jmxremote.password 或使用常规 InputStream 将其作为类路径资源进行访问,例如通过使用Class.getResourceAsStream():

    try (InputStream in : getClass().getResourceAsStream("/jmxremote.password")) {
      // read input
    }
    

    【讨论】:

      【解决方案2】:

      TL;DR

      在 Spring Framework 环境中,专注于使用 Spring 实用工具来处理资源(如 ResourceUtils class),它很好地封装了较低级别的、依赖于操作系统的 IO 操作。 ResourceUtils 已经包含多个陷阱,以确定您正在运行的项目是 exploded(在 IDE 中运行)还是 打包(在 JAR 文件中)。


      Karol 提供的答案似乎是最简单的并且相对防弹,直到您需要一定程度的灵活性来指定文件位置(在 jar 文件内,但可以在外部定义它并在文件系统中的某个位置提供)。那么使用getResourceAsStream()方法的方法就行不通了。

      标准 Java IO (java.nio) 使用 FileSystemProvider 类来委派 IO 操作(如创建、读取和删除文件)。

      提供者由 URI方案 标识。默认提供程序由 URI 方案“文件”标识。它创建 FileSystem,提供对 Java 虚拟机可访问的文件系统的访问。 FileSystems 类定义如何定位和加载文件系统提供程序。

      因此,如果您的文件位于文件系统的某个位置,则没有问题,并且一切正常。从技术上讲,Application.class.getResource("").toURI() 返回的 URL 以 file:// 开头并包含有效的文件系统路径。

      话虽如此,当您的文件“降落”在 jar 文件中时,Application.class.getResource("").toURI() 返回的内容更像 file://{jar-location}!/(注意感叹号) ,这不是有效的文件模式路径,Java 不知道如何处理它。需要注册一个额外的文件系统提供程序。

      FileSystems.newFileSystem(uri, emptyMap());
      

      Java 计算出(基于 URI)方案并注册一个新的文件系统。从现在开始,可以使用标准的java.nio 文件操作。

      例如,如果您在 /webapp 文件夹中有一些文件可以(但不需要)在 jar 文件中,并且您想列出它们。

      // Load zip specific filesystem provider when run from inside a fat-jar
      URI uri = Application.class.getResource("").toURI();
      if (uri.toString().contains("!")) {
          FileSystems.newFileSystem(uri, emptyMap());
      }
      
      URI rootFolder = Application.class.getResource("/webapp").toURI();
      List<Path> banners = Files.list(Paths.get(rootFolder))
              .collect(Collectors.toList());
      
      Random rand = new Random();
      Path path = banners.get(rand.nextInt(banners.size()));
      
      log.info("Random image: {}", path.getFileName());
      byte[] bytes = Files.readAllBytes(path);
      

      新文件系统提供程序的安装是全局性的,应该只安装一次。

      【讨论】:

      • 独立 Spring Boot 应用程序在找到 ZipDirectoryStream 时失败,并出现 java.nio.file.NotDirectoryException。我的观点不是强调对 jar 捆绑资源的基于 File/FileSystem 的访问,而是使用 ResourceLoader 或 ClasspathResource 来访问资源的输入流。大多数时候,全功能的随机访问文件属于 YAGNI。
      猜你喜欢
      • 2021-03-05
      • 2017-10-07
      • 2017-06-18
      • 2019-02-19
      • 1970-01-01
      • 1970-01-01
      • 2015-06-10
      • 2018-02-03
      • 2020-02-14
      相关资源
      最近更新 更多