«1. Обзор
В этом руководстве мы рассмотрим проблему, которая может возникнуть при чтении файлов ресурсов в приложении Java: во время выполнения папка ресурсов редко находится в том же месте на диске, что и в нашем исходном коде. .
Давайте посмотрим, как Java позволяет нам получать доступ к файлам ресурсов после того, как наш код упакован.
2. Чтение файлов
Допустим, наше приложение читает файл во время запуска:
try (FileReader fileReader = new FileReader("src/main/resources/input.txt");
BufferedReader reader = new BufferedReader(fileReader)) {
String contents = reader.lines()
.collect(Collectors.joining(System.lineSeparator()));
}
Если мы запустим приведенный выше код в среде IDE, файл загрузится без ошибки. Это связано с тем, что наша IDE использует каталог нашего проекта в качестве текущего рабочего каталога, а каталог src/main/resources находится прямо там, чтобы приложение могло его прочитать.
Теперь предположим, что мы используем подключаемый модуль Maven JAR для упаковки нашего кода в виде JAR.
Когда мы запустим его из командной строки:
java -jar core-java-io2.jar
Мы увидим следующую ошибку:
Exception in thread "main" java.io.FileNotFoundException:
src/main/resources/input.txt (No such file or directory)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at java.io.FileInputStream.<init>(FileInputStream.java:93)
at java.io.FileReader.<init>(FileReader.java:58)
at com.baeldung.resource.MyResourceLoader.loadResourceWithReader(MyResourceLoader.java:14)
at com.baeldung.resource.MyResourceLoader.main(MyResourceLoader.java:37)
3. Исходный код и скомпилированный код
Когда мы собираем JAR, ресурсы помещаются в корневой каталог упакованных артефактов.
В нашем примере мы видим, что установка исходного кода имеет input.txt в src/main/resources в нашем каталоге исходного кода.
Однако в соответствующей структуре JAR мы видим:
META-INF/MANIFEST.MF
META-INF/
com/
com/baeldung/
com/baeldung/resource/
META-INF/maven/
META-INF/maven/com.baeldung/
META-INF/maven/com.baeldung/core-java-io-files/
input.txt
com/baeldung/resource/MyResourceLoader.class
META-INF/maven/com.baeldung/core-java-io-files/pom.xml
META-INF/maven/com.baeldung/core-java-io-files/pom.properties
Здесь input.txt находится в корневом каталоге JAR. Итак, когда код выполнится, мы увидим исключение FileNotFoundException.
Даже если бы мы изменили путь к /input.txt, исходный код не смог бы загрузить этот файл, поскольку ресурсы обычно не адресуются как файлы на диске. Файлы ресурсов упакованы внутри JAR, поэтому нам нужен другой способ доступа к ним.
4. Ресурсы
Давайте вместо этого используем загрузку ресурсов для загрузки ресурсов из пути к классам, а не из определенного местоположения файла. Это будет работать независимо от того, как упакован код:
try (InputStream inputStream = getClass().getResourceAsStream("/input.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
String contents = reader.lines()
.collect(Collectors.joining(System.lineSeparator()));
}
ClassLoader.getResourceAsStream() просматривает путь к классам для данного ресурса. Начальная косая черта на входе для getResourceAsStream() указывает загрузчику читать из базы пути к классам. Содержимое нашего файла JAR находится в пути к классам, поэтому этот метод работает.
IDE обычно включает src/main/resources в свой путь к классам и, таким образом, находит файлы.
5. Заключение
В этой быстрой статье мы реализовали загрузку файлов как ресурсов пути к классам, чтобы наш код работал согласованно, независимо от того, как он был упакован.
Как всегда, код примера доступен на GitHub.