Когда мы создаем среду выполнения Java с помощью jlink, она берет все классы / ресурсы Java и помещает их в файл образа JRT: lib/modules
.
Вот базовая структура ресурсов проекта Maven, которую я использую:
src
main
resources
dict
xkcd_en
Я просто пытаюсь прочитать xkcd_en
текстовый файл. Если мы посмотрим на файл JRT, вот он:
>> jimage list /path/to/lib/modules
...
Module: main
dict/xkcd_en
...
Также я явно открыл его в module-info
, на всякий случай:
module main {
opens dict;
// ..rest code omitted
}
Единственный способ прочитать файл - получить его как поток ввода:
РАБОТАЕТ:
public static InputStream getResourceAsStream(String resource) {
return FileUtils.class.getResourceAsStream(resource);
}
System.out.println(new BufferedReader(
new InputStreamReader(getResourceAsStream("/dict/xkcd_en")))
.lines().collect(Collectors.joining("\n"))
);
НЕ РАБОТАЕТ:
Но если я пытаюсь получить URI файла и прочитать его через API Java NIO, это не сработает:
public static URL getResourceOrThrow(String resource) {
URL url = FileUtils.class.getResource(resource);
Objects.requireNonNull(url);
return url;
}
1 - Java NIO не может найти файл. Но он определенно существует, в противном случае getResource()
возвращает null
.
System.out.println(Paths.get(getResourceOrThrow("/dict/xkcd_en").toURI()));
// /main/dict/xkcd_en
Files.readAllLines(Paths.get(getResourceOrThrow("/dict/xkcd_en").toURI()));
Caused by: java.nio.file.NoSuchFileException: /main/dict/xkcd_en
at java.base/jdk.internal.jrtfs.JrtFileSystem.checkNode(JrtFileSystem.java:494)
at java.base/jdk.internal.jrtfs.JrtFileSystem.getFileContent(JrtFileSystem.java:253)
at java.base/jdk.internal.jrtfs.JrtFileSystem.newInputStream(JrtFileSystem.java:342)
at java.base/jdk.internal.jrtfs.JrtPath.newInputStream(JrtPath.java:631)
at java.base/jdk.internal.jrtfs.JrtFileSystemProvider.newInputStream(JrtFileSystemProvider.java:322)
2 - То же самое, если вы будете использовать FileSystem
напрямую:
FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
System.out.println(fs.getPath("main/dict/xkcd_en"));
// main/dict/xkcd_en
Files.readAllLines(fs.getPath("main/dict/xkcd_en")));
Caused by: java.nio.file.NoSuchFileException: /main/dict/xkcd_en
at java.base/jdk.internal.jrtfs.JrtFileSystem.checkNode(JrtFileSystem.java:494)
3 - Java NIO даже не знает, что такое схема jrt:/
.
Files.readAllLines(Paths.get(getResourceOrThrow("/dict/xkcd_en").toExternalForm()));
Caused by: java.nio.file.InvalidPathException: Illegal char <:> at index 3: jrt:/main/dict/xkcd_en
at java.base/sun.nio.fs.WindowsPathParser.normalize(WindowsPathParser.java:182)
at java.base/sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:153)
at java.base/sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:77)
at java.base/sun.nio.fs.WindowsPath.parse(WindowsPath.java:92)
at java.base/sun.nio.fs.WindowsFileSystem.getPath(WindowsFileSystem.java:229)
at java.base/java.nio.file.Path.of(Path.java:147)
at java.base/java.nio.file.Paths.get(Paths.java:69)
Вот спецификации JRT FS.
URL-адрес jrt - это иерархический URI, согласно RFC 3986, с синтаксисом
ОГД: / [$ MODULE [/ $ PATH]]
где $ MODULE - необязательное имя модуля, а $ PATH, если присутствует, -
путь к определенному классу или файлу ресурса в этом модуле.
Значение URL-адреса JRT зависит от его структуры:
jrt:/$MODULE/$PATH refers to the specific class or resource file named $PATH within the given $MODULE.
jrt:/$MODULE refers to all of the class and resource files in the module $MODULE.
jrt:/ refers to the entire collection of class and resource files stored in the current run-time image.
Полученный путь мне подходит. Где я не прав?