java чтение ресурсов Maven из resources
в моем учебном maven проекте есть такой класс, который читает содержимое файла и возвращает лист стрингов:
public class FileReader {
public List<String> readFile(String fileName) {
if (fileName == null) {
throw new IllegalArgumentException("fileName can't be null");
}
URL url = getClass().getClassLoader().getResource(fileName);
if (url == null) {
throw new ReadFileException("file " + fileName + " was not found or access to the resource is denied");
}
try (Stream<String> stream = Files.lines(Paths.get(url.toURI()))) {
return stream.collect(Collectors.toList());
} catch (IOException | URISyntaxException e) {
throw new ReadFileException("error occurs opening the file " + fileName, e);
}
}
}
этот класс читает кое-какие файлы из папки myproject\src\main\resources и потом листы дальше передаются в другие классы. Все работает нормально, пока запускается в ide. maven без проблем собирает проект в jar, проходят тесты и тд, но, как только пытаюсь запустить этот jar файл в консоли (java -jar myproject-0.0.1-SNAPSHOT) - летит ошибка:
Exception in thread "main" java.nio.file.FileSystemNotFoundException at com.sun.nio.zipfs.ZipFileSystemProvider.getFileSystem(ZipFileSystemProvider.java:171) at com.sun.nio.zipfs.ZipFileSystemProvider.getPath(ZipFileSystemProvider.java:157) at java.nio.file.Paths.get(Unknown Source) at org.project.input.FileReader.readFile(FileReader.java:24) at org.project.MainClass.main(MainClass.java:31)
Я понимаю, что проблема где-то здесь :getClass().getClassLoader().getResource(fileName)
но не могу понять в чем, помогите плиз, если кто знает
Ответы (1 шт):
Проблема в том, что при упаковке в jar, файл, который запакован не существует в файловой системе, как собственно файл. То есть он недоступен ни по одному пути в файловой системе. URL полученый с помощью getClass().getClassLoader().getResource указывает внутрь jar, он состоит из двух частей: путь к jar + путь внутри jar.
Таким образом доступ с помощью Paths или File просто используя этот URL - это непереносимый способ доступаться к ресурсу. Он работает, когда ресурс существует как файл, но не будет работать, когда он упакован в архив jar.
Переносимый способ - это использовать getResourceAsStream:
InputStream in = this.getClass()
.getClassLoader().getResourceAsStream("/file.txt");
чтобы получить InputStream для файла ресурса, который находится в корне resources. И дальше уже читать содержимое из этого потока.
Реализация getResourceAsStream в каждом ClassLoader своя, и таким образом доступ к ресурсам будь-то классы или другие файлы работает и в случае упаковки их в jar - в этом случае используется специфичный для jar ClassLoader.