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 шт):

Автор решения: Roman Konoval

Проблема в том, что при упаковке в 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.

→ Ссылка