Як отримати шлях до ресурсу у файлі Java JAR


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)

Чи є спосіб отримати шлях до файлу ресурсу?


1
Так. У мене є клас, з яким я хотів би працювати з обома, папку ззовні (у випадку, якщо я хочу змінити якийсь параметр конфігураційного файлу) та JAR, який приховує користувачеві конфігураційні файли реалізації (наприклад, розподільний JAR усім людям).
no_ripcord

1
Тож клас просто отримує PATH у файл (файл config).
no_ripcord

3
Тоді ви, мабуть, повинні мати цей клас мати справу з потоком введення, який ви можете отримати з будь-якого джерела.
Карл Манастер

1
Так, я знаю. Але це було б більш чітко і чисто в інший спосіб. Але все одно.
no_ripcord

1
Ви намагаєтесь зробити щось подібне до варіанту №4 у цьому питанні? stackoverflow.com/questions/775389 / ...
Еріксон

Відповіді:


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!");
}

3
+1 Це працювало для мене. Переконайтесь, що ви розмістите файли, які ви хочете прочитати, у binпапку та перейдіть до каталогу завантаження класу в ресурсах, перш ніж використовувати шлях `/com/myorg/filename.ext '.
rayryeng

+1 Це також працює для мене ... Я розумію, що можуть виникнути певні ризики для безпеки, пов'язані з таким підходом, тому власники програм повинні знати про це.
ЛукасА

Ви могли б пояснити це твердження "Завжди краще завантажувати ресурс за допомогою getResourceAsStream ()"? Як це може забезпечити вирішення проблеми?
Лука С.

@LucaS. Це було призначене лише для випадків зображень, вибачте, це було не дуже ясно. Підхід повинен бути незалежним від платформи, хоча це трохи хитромудрий спосіб.
Томбарт

@LucasA Чи можете ви уточнити згадані вами ризики?
ZX9

25

Відповідь в одному рядку -

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

В основному getResourceметод дає URL-адресу. З цієї URL-адреси ви можете дістати шлях, зателефонувавшиtoExternalForm()

Список літератури:

getResource () , toEternalternal Form ()


7
Працюючи з мого середовища (IntelliJ), це створює просту URL-адресу файлу, яка працює у всіх випадках. Однак під час запуску з самої банки я отримую URI, схожий на jar: file: /path/to/jar/jarname.jar! /File_in_jar.mp4. Не все може використовувати URI, що починається з jar. Справа в точці медіа JavaFX.
Ной Тернульо

1
Мені найбільше подобається ця відповідь. Зрозуміло, що в більшості випадків може бути краще просто захопити InputStream до ресурсу, коли він знаходиться у файлі jar, але якщо з якихось причин вам справді потрібен шлях, це те, що працює. Мені потрібен був шлях, щоб надати сторонній об’єкт. Отже, дякую!
Маріо

1
вищевказане рішення не працює, коли в IDE, в Intellijit додає file:/шлях, який працює в банку, але не в 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

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


Коли я намагаюся конвертувати URL цього формату (... bot.jar! / Config / ...) в URI, він говорить, що шлях не є ієрархічним.
gEdringer

7

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

src/main/resources/myfile.txt

Якщо ви скинете свою банку за допомогою, jar -tvf myjar.jar ви побачите вихідний та відносний шлях у файлі jar та скористаєтеся FORWARD SLASHES.


Вам справді доводиться використовувати косою косою рискою, навіть у Windows. Це означає, що ви не можете використовувати File.separator.
вул.

3

У моєму випадку я використовував об’єкт URL замість Шлях.

Файл

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

Ресурс у classpath за допомогою classloader

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

Коли мені потрібно прочитати вміст, я можу використовувати наступний код:

InputStream stream = url.openStream();

І ви можете отримати доступ до вмісту за допомогою InputStream.


3

Це той самий код користувача Tombart із потоком потоку та близько, щоб уникнути неповної копії тимчасового вмісту файлу з ресурсу jar та мати унікальні імена файлів temp.

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
private static final String FILE_LOCATION = "com/input/file/somefile.txt";

//Method Body


InputStream invalidCharacterInputStream = URLClassLoader.getSystemResourceAsStream(FILE_LOCATION);

Отримати це getSystemResourceAsStream- найкращий варіант. Отримуючи вхідний потік, а не файл або URL-адресу, він працює у файлі JAR і як окремий.



0

У файлі jar ресурс знаходиться абсолютно в ієрархії пакунків (не ієрархія файлової системи). Отже, якщо у вас є клас com.example.Sweet, завантажуючи ресурс з назвою "./default.conf", то ім'я ресурсу визначається як "/com/example/default.conf".

Але якщо він знаходиться в банці, то це не Файл ...


0

Всередині папки ресурсів (java / main / ресурси) вашої баночки додайте файл (ми припускаємо, що ви додали XML-файл під назвою import.xml ), після чого ви вводите, ResourceLoaderякщо ви використовуєте весну, як нижче

@Autowired
private ResourceLoader resourceLoader;

всередині функції tour введіть наступний код, щоб завантажити файл:

    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;
    }
}

Ви пропускаєте якийсь контекст? Це дає мені:java.lang.NullPointerException: Attempt to invoke virtual method 'java.security.CodeSource java.security.ProtectionDomain.getCodeSource()' on a null object reference
Allen Luce

Я відредагував відповідь і написав весь метод, який я використовував у програмному забезпеченні
gbii

0

слідкуйте за кодом!

/ src / main / ресурси / файл

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;
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.