Ресурс Java як файл


122

Чи існує спосіб у Java побудувати екземпляр File на ресурсі, отриманому з банку через завантажувач класів?

Моя програма використовує деякі файли з jar (за замовчуванням) або з каталогу файлової системи, визначеного під час виконання (введення користувача). Я шукаю послідовний спосіб
a) завантаження цих файлів у потоці
b) переліку файлів у визначеній користувачем каталозі або в каталозі відповідно до jar

Редагувати: Мабуть, ідеальним підходом було б триматися подалі від java.io.File взагалі. Чи є спосіб завантажити каталог з classpath і перерахувати його вміст (файли / сутності, що містяться в ньому)?

Відповіді:


58

ClassLoader.getResourceAsStreamі Class.getResourceAsStream, безумовно, шлях для завантаження даних про ресурси. Однак я не вірю, що існує спосіб "перерахувати" вміст елемента classpath.

У деяких випадках це може бути просто неможливо - наприклад, файл ClassLoader може генерувати дані на льоту, виходячи з того, яке ім'я ресурсу вимагається. Якщо ви подивитеся на ClassLoaderAPI (який в основному працює над механізмом classpath), ви побачите, що не можна робити все, що ви хочете.

Якщо ви знаєте, що у вас фактично є файл jar, ви можете завантажити його, ZipInputStreamщоб дізнатися, що є в наявності. Це означає, що у вас буде різний код для каталогів та файлів jar.

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


3
Я погоджуюсь, що це дратує - але це робить ClassLoader більш широко застосовно до інших способів. Наприклад, легко написати "веб-завантажувач", тому що Інтернет хороший для отримання файлів, але зазвичай не перераховує файли.
Джон Скіт

Здається, що якщо ресурс, який ви намагаєтеся завантажити, набагато більший за 1 Мбіт, то InputStreamви отримуєте від getResourceAsStreamзупинок для отримання байтів ресурсу після цього розміру, а натомість повертає 0, якщо він міститься в стиснутій файловій системі, як jar. Ви, мабуть, змушені використовувати getResourceта завантажувати файл незалежно від цього в цьому випадку.
mgttlinger

1
@mgttlinger: Мені це здається малоймовірним ... напевно, варто поставити нове запитання з докором, якщо зможете.
Джон Скіт

Проблема була пов’язана з readметодом вирішення кількості даних для читання (я не знав про це). І здається, що весь файл читається, якщо файл, який читається, розташований у звичайній папці. Якщо файл знаходиться в банці, тому що він був упакований, він читає лише його частини.
mgttlinger

1
@mgttlinger: Ні, він не буде читати лише його частини - але він може не заповнювати весь буфер, наданий під час кожного виклику читання. Як завжди InputStream, якщо ви хочете прочитати весь ресурс, вам слід циклічно, поки не readповернеться -1.
Джон Скіт

98

У мене була така ж проблема, і я міг використовувати наступне:

// Load the directory as a resource
URL dir_url = ClassLoader.getSystemResource(dir_path);
// Turn the resource into a File object
File dir = new File(dir_url.toURI());
// List the directory
String files = dir.list()

45
Цей підхід не працює з JAR на classpath, лише з файлами та каталогами
yegor256,

1
@ yegor256 Якщо ваш файл знаходиться в банку, ви можете створити FileSystemоб'єкт і отримати Pathз нього. Тоді ви можете прочитати його, як зазвичай. (Див stackoverflow.com/a/22605905/1876344 )
mgttlinger

53

Ось трохи коду з одного з моїх додатків ... Повідомте мене, чи відповідає вашим потребам. Ви можете використовувати це, якщо знаєте файл, який ви хочете використовувати.

URL defaultImage = ClassA.class.getResource("/packageA/subPackage/image-name.png");
File imageFile = new File(defaultImage.toURI());

Сподіваюся, що це допомагає.


Так, це так, але toURI()не вдасться.
Олів’є

10

Надійний спосіб побудувати екземпляр файлу на ресурсі, отриманому з банку, - це скопіювати ресурс як потік у тимчасовий Файл (тимчасовий файл буде видалено, коли закривається JVM):

public static File getResourceAsFile(String resourcePath) {
    try {
        InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(resourcePath);
        if (in == null) {
            return null;
        }

        File tempFile = File.createTempFile(String.valueOf(in.hashCode()), ".tmp");
        tempFile.deleteOnExit();

        try (FileOutputStream out = new FileOutputStream(tempFile)) {
            //copy stream
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, bytesRead);
            }
        }
        return tempFile;
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
}

4

Спробуйте це:

ClassLoader.getResourceAsStream ("some/pkg/resource.properties");

Доступні інші методи, наприклад, дивіться тут: http://www.javaworld.com/javaworld/javaqa/2003-08/01-qa-0808-property.html


Дякую за Вашу відповідь. Я знаю про getResourceAsStream, але не думаю, що через нього я можу завантажити каталог з classpath.
Мантрум

У каталозі Java є файл (я думаю, коріння UNIX), тож вам слід хоча б спробувати.
топчеф

Я думаю, щоб перелічити файли в каталозі, який вам знадобиться використовувати java.io.File. Ви можете шукати файли за допомогою ClassLoader.findResource, який повертає URL-адресу, яка може бути передана у файл.
Натан Фоксленд

2

4
Хоча це посилання може відповісти на питання, бажано було б сюди включити істотні частини відповіді та надати посилання для посилання, оскільки посилання може змінитися або пов’язана сторінка може бути видалена, тим самим зробити відповідь марною.
JonasCz
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.