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);
新文件系统提供程序的安装是全局性的,应该只安装一次。