Как получить путь к ресурсу в файле JAR Java - PullRequest
151 голосов
/ 03 июня 2009

Я пытаюсь найти путь к ресурсу, но мне не повезло.

Это работает (как в IDE, так и с JAR), но таким образом я не могу получить путь к файлу, только содержимое файла:

ClassLoader classLoader = getClass().getClassLoader();
PrintInputStream(classLoader.getResourceAsStream("config/netclient.p"));

Если я сделаю это:

ClassLoader classLoader = getClass().getClassLoader();
File file = new File(classLoader.getResource("config/netclient.p").getFile());

Результат: java.io.FileNotFoundException: file:/path/to/jarfile/bot.jar!/config/netclient.p (No such file or directory)

Есть ли способ получить путь к файлу ресурса?

Ответы [ 16 ]

68 голосов
/ 03 июня 2009

Это умышленно. Содержимое «файла» может быть недоступно в виде файла. Помните, что вы имеете дело с классами и ресурсами, которые могут быть частью файла JAR или другого типа ресурса. Загрузчик классов не должен предоставлять дескриптор файла для ресурса, например, файл jar, возможно, не был расширен в отдельные файлы в файловой системе.

Все, что вы можете сделать, получив файл java.io.File, можно сделать, скопировав поток во временный файл и сделав то же самое, если файл java.io.File абсолютно необходим.

53 голосов
/ 30 января 2013

При загрузке ресурса убедитесь, что вы заметили разницу между:

getClass().getClassLoader().getResource("com/myorg/foo.jpg") //relative path

и

getClass().getResource("/com/myorg/foo.jpg")); //note the slash at the beginning

Полагаю, эта путаница вызывает большинство проблем при загрузке ресурса.


Кроме того, когда вы загружаете изображение, его проще использовать getResourceAsStream():

BufferedImage image = ImageIO.read(getClass().getResourceAsStream("/com/myorg/foo.jpg"));

Если вам действительно нужно загрузить (не образный) файл из архива JAR, вы можете попробовать это:

    File file = null;
    String resource = "/com/myorg/foo.xml";
    URL res = getClass().getResource(resource);
    if (res.getProtocol().equals("jar")) {
        try {
            InputStream input = getClass().getResourceAsStream(resource);
            file = File.createTempFile("tempfile", ".tmp");
            OutputStream out = new FileOutputStream(file);
            int read;
            byte[] bytes = new byte[1024];

            while ((read = input.read(bytes)) != -1) {
                out.write(bytes, 0, read);
            }
            out.close();
            file.deleteOnExit();
        } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
        }
    } else {
        //this will probably work in your IDE, but not from a JAR
        file = new File(res.getFile());
    }

    if (file != null && !file.exists()) {
        throw new RuntimeException("Error: File " + file + " not found!");
    }
20 голосов
/ 26 ноября 2014

Ответ в одну строку -

String path = this.getClass().getClassLoader().getResource(<resourceFileName>).toExternalForm()

В основном getResource метод дает URL. Из этого URL вы можете извлечь путь, позвонив по номеру toExternalForm()

Ссылки:

getResource () , toExternalForm ()

11 голосов
/ 18 марта 2011

Я потратил некоторое время на эту проблему, потому что ни одно решение, которое я нашел, на самом деле не работало, как ни странно! Рабочий каталог часто не является каталогом JAR, особенно если JAR (или любая другая программа) запускается из меню «Пуск» в Windows. Итак, вот что я сделал, и он работает для файлов .class, запускаемых извне JAR, так же, как и для JAR. (Я тестировал только под Windows 7.)

try {
    //Attempt to get the path of the actual JAR file, because the working directory is frequently not where the file is.
    //Example: file:/D:/all/Java/TitanWaterworks/TitanWaterworks-en.jar!/TitanWaterworks.class
    //Another example: /D:/all/Java/TitanWaterworks/TitanWaterworks.class
    PROGRAM_DIRECTORY = getClass().getClassLoader().getResource("TitanWaterworks.class").getPath(); // Gets the path of the class or jar.

    //Find the last ! and cut it off at that location. If this isn't being run from a jar, there is no !, so it'll cause an exception, which is fine.
    try {
        PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(0, PROGRAM_DIRECTORY.lastIndexOf('!'));
    } catch (Exception e) { }

    //Find the last / and cut it off at that location.
    PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(0, PROGRAM_DIRECTORY.lastIndexOf('/') + 1);
    //If it starts with /, cut it off.
    if (PROGRAM_DIRECTORY.startsWith("/")) PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(1, PROGRAM_DIRECTORY.length());
    //If it starts with file:/, cut that off, too.
    if (PROGRAM_DIRECTORY.startsWith("file:/")) PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(6, PROGRAM_DIRECTORY.length());
} catch (Exception e) {
    PROGRAM_DIRECTORY = ""; //Current working directory instead.
}
8 голосов
/ 03 июня 2009

, если netclient.p находится внутри файла JAR, у него не будет пути, потому что этот файл находится внутри другого файла. в этом случае лучший путь, который вы можете выбрать, это действительно file:/path/to/jarfile/bot.jar!/config/netclient.p.

6 голосов
/ 29 декабря 2010

Вам необходимо понять путь в файле jar.
Просто обратитесь к нему относительно. Так что если у вас есть файл (myfile.txt), расположенный в foo.jar в каталоге \src\main\resources (стиль maven). Вы бы назвали это как:

src/main/resources/myfile.txt

Если вы выбросите банку с помощью jar -tvf myjar.jar, вы увидите выходные данные и относительный путь в файле банки и воспользуетесь ПРЯМОЙ СЛЭШ.

2 голосов
/ 02 марта 2017

В моем случае я использовал объект URL вместо Path.

Файл

File file = new File("my_path");
URL url = file.toURI().toURL();

Ресурс в classpath с использованием загрузчика классов

URL url = MyClass.class.getClassLoader().getResource("resource_name")

Когда мне нужно прочитать содержимое, я могу использовать следующий код:

InputStream stream = url.openStream();

И вы можете получить доступ к содержимому с помощью InputStream.

2 голосов
/ 25 февраля 2015

Это тот же код от пользователя Tombart с потоковым сбросом и закрытием, чтобы избежать неполного копирования содержимого временного файла с ресурса jar и иметь уникальные имена временных файлов.

     File file = null;
        String resource = "/view/Trial_main.html" ;
        URL res = getClass().getResource(resource);
        if (res.toString().startsWith("jar:")) {
            try {
                InputStream input = getClass().getResourceAsStream(resource);
                file = File.createTempFile(new Date().getTime()+"", ".html");
                OutputStream out = new FileOutputStream(file);
                int read;
                byte[] bytes = new byte[1024];

                while ((read = input.read(bytes)) != -1) {
                    out.write(bytes, 0, read);
                }
                out.flush();
                out.close();
                input.close();
                file.deleteOnExit();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        } else {
            //this will probably work in your IDE, but not from a JAR
            file = new File(res.getFile());
        }
2 голосов
/ 03 июня 2009

Файл - это абстракция для файла в файловой системе, и файловые системы ничего не знают о содержимом JAR.

Попробуйте использовать URI, я думаю, что есть протокол jar: //, который может быть полезен для ваших целей.

1 голос
/ 17 ноября 2018

Может быть немного поздно, но вы можете использовать мою библиотеку KResourceLoader , чтобы получить ресурс из вашей банки:

File resource = getResource("file.txt")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...