Как получить путь к ресурсу в файле JAR Java

166

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

Это работает (как в 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)

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

no_ripcord
источник
1
Да. У меня есть класс, с которым я хотел бы работать, как папка снаружи (на случай, если я захочу изменить какой-либо параметр файла конфигурации), так и JAR, который скрывает файлы конфигурации реализации для пользователя (как распространяемый файл) Банку всем людям).
no_ripcord
1
Таким образом, класс просто получает PATH в файл (файл конфигурации).
no_ripcord
3
Тогда вы, вероятно, должны иметь дело с этим потоком ввода, который вы можете получить из любого источника.
Карл Манастер
1
Да, я знаю. Но это было бы более ясно и чисто с другой стороны. Но все равно.
no_ripcord
1
Вы пытаетесь сделать что-то вроде варианта № 4 в этом вопросе? stackoverflow.com/questions/775389/…
erickson

Ответы:

71

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

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

Нил Малони
источник
6
Вы можете добавить 'rsrc:', когда вы вызываете свой ресурс, чтобы открыть его. как новый файл ("rsrc: filename.txt"), он будет загружать filename.txt, который упакован в корень вашего фляги
gipsh
63

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

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!");
}
Tombart
источник
3
+1 Это сработало для меня. binПрежде чем использовать путь `/com/myorg/filename.ext ', убедитесь, что вы поместили файлы, которые вы хотите прочитать, в папку и перейдите в каталог загрузки классов в ресурсах.
Rayryeng
+1 Это также работает для меня ... Я понимаю, что могут быть некоторые риски безопасности, связанные с этим подходом, поэтому владельцы приложений должны знать об этом.
LucasA
Не могли бы вы уточнить это утверждение «Всегда лучше загружать ресурс с помощью getResourceAsStream ()»? Как это может обеспечить решение проблемы?
Лука С.
@LucaS. Это было предназначено только для случая изображений, извините, это не было очень ясно. Подход должен быть независимым от платформы, хотя это немного хакерский способ.
Томбарт
@ LucasA Не могли бы вы уточнить риски, которые вы упомянули?
ZX9
25

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

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

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

Ссылки:

getResource () , toExternalForm ()

Манойкумар Хотеле
источник
7
При запуске из моей среды (IntelliJ) это создает простой URL-адрес файла, который работает во всех случаях. Однако при запуске из самого jar я получаю URI, похожий на jar: file: /path/to/jar/jarname.jar! /File_in_jar.mp4. Не все могут использовать URI, который начинается с jar. Показательный пример JavaFX Media.
Ноа Тернулло
1
Мне больше нравится этот ответ. Конечно, в большинстве случаев может быть предпочтительнее просто захватить InputStream к ресурсу, когда он находится в файле jar, но если по какой-то причине вам действительно нужен путь, это то, что работает. Мне нужен был путь к стороннему объекту. Так что спасибо!
Марио
1
вышеупомянутое решение не работает в то время как в IDE, в Intellijit добавляет file:/к пути, который работает в jar, но не в IDE
Tayab Hussain
Ваш ответ решил проблему, над которой я пытался работать не менее 2 дней. TYVM !!
Джонатан
12

Я потратил некоторое время, возившись с этой проблемой, потому что ни одно решение, которое я нашел, на самом деле не работало, как ни странно! Рабочий каталог часто не является каталогом 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

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

CD1
источник
Когда я пытаюсь преобразовать URL-адрес этого формата (... bot.jar! / Config / ...) в URI, он говорит, что путь не является иерархическим.
gEdringer
7

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

src/main/resources/myfile.txt

Если вы сбросите свой jar-файл, используя его, jar -tvf myjar.jar вы увидите выходные данные и относительный путь в файле jar-файла и воспользуетесь FORWARD SLASHES.

Эрик Мэнли
источник
Вы действительно должны использовать косую черту даже в Windows. Это означает, что вы не можете использовать File.separator.
ул
3

В моем случае я использовал объект 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.

jmgoyesc
источник
3

Это тот же код от пользователя 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

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

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

Фортран
источник
1

Следующий путь работал для меня: classpath:/path/to/resource/in/jar

Анатолий
источник
1
@Anatoly Не могли бы вы поделиться более подробными данными выше
Kasun Siyambalapitiya
1
private static final String FILE_LOCATION = "com/input/file/somefile.txt";

//Method Body


InputStream invalidCharacterInputStream = URLClassLoader.getSystemResourceAsStream(FILE_LOCATION);

Получение этого getSystemResourceAsStream- лучший вариант. Получая входной поток, а не файл или URL, работает в JAR-файле и как отдельный.

Shano
источник
1

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

File resource = getResource("file.txt")
Ламберто Басти
источник
0

Находясь в файле JAR, ресурс находится абсолютно в иерархии пакетов (не в иерархии файловой системы). Поэтому, если у вас есть класс com.example.Sweet, загружающий ресурс с именем «./default.conf», тогда имя ресурса указывается как «/com/example/default.conf».

Но если это в банке, то это не файл ...

Вилнис Круминьш
источник
0

В вашей папке ресурсов (java / main / resources) вашего jar добавьте свой файл (мы предполагаем, что вы добавили XML-файл с именем import.xml ), после чего вы вводитеResourceLoader если используете пружину, как показано ниже.

@Autowired
private ResourceLoader resourceLoader;

внутри функции тура напишите ниже код для загрузки файла:

    Resource resource = resourceLoader.getResource("classpath:imports.xml");
    try{
        File file;
        file = resource.getFile();//will load the file
...
    }catch(IOException e){e.printStackTrace();}
БЕРГУИГА Мохамед Амин
источник
0

Возможно, этот метод можно использовать для быстрого решения.

public class TestUtility
{ 
    public static File getInternalResource(String relativePath)
    {
        File resourceFile = null;
        URL location = TestUtility.class.getProtectionDomain().getCodeSource().getLocation();
        String codeLocation = location.toString();
        try{
            if (codeLocation.endsWith(".jar"){
                //Call from jar
                Path path = Paths.get(location.toURI()).resolve("../classes/" + relativePath).normalize();
                resourceFile = path.toFile();
            }else{
                //Call from IDE
                resourceFile = new File(TestUtility.class.getClassLoader().getResource(relativePath).getPath());
            }
        }catch(URISyntaxException ex){
            ex.printStackTrace();
        }
        return resourceFile;
    }
}
GBII
источник
Вы опускаете какой-то контекст? Это дает мне:java.lang.NullPointerException: Attempt to invoke virtual method 'java.security.CodeSource java.security.ProtectionDomain.getCodeSource()' on a null object reference
Аллен Люс
Я отредактировал ответ и написал весь метод, который я использовал в программном обеспечении
gbii
0

следуйте коду!

/ SRC / главная / ресурсы / файл

streamToFile(getClass().getClassLoader().getResourceAsStream("file"))

public static File streamToFile(InputStream in) {
    if (in == null) {
        return null;
    }

    try {
        File f = File.createTempFile(String.valueOf(in.hashCode()), ".tmp");
        f.deleteOnExit();

        FileOutputStream out = new FileOutputStream(f);
        byte[] buffer = new byte[1024];

        int bytesRead;
        while ((bytesRead = in.read(buffer)) != -1) {
            out.write(buffer, 0, bytesRead);
        }

        return f;
    } catch (IOException e) {
        LOGGER.error(e.getMessage(), e);
        return null;
    }
}
seunggabi
источник