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


122

Есть ли способ в Java создать экземпляр File на ресурсе, полученном из jar через загрузчик классов?

Мое приложение использует некоторые файлы из jar (по умолчанию) или из каталога файловой системы, указанного во время выполнения (пользовательский ввод). Я ищу последовательный способ
а) загрузки этих файлов в виде потока
б) перечисления файлов в определяемом пользователем каталоге или каталоге в банке соответственно

Изменить: По-видимому, идеальным подходом было бы вообще держаться подальше от java.io.File. Есть ли способ загрузить каталог из пути к классам и перечислить его содержимое (файлы / объекты, содержащиеся в нем)?

Ответы:


58

ClassLoader.getResourceAsStreamи Class.getResourceAsStreamопределенно являются способом загрузки данных ресурсов. Однако я не верю, что есть какой-либо способ «перечислить» содержимое элемента пути к классам.

В некоторых случаях это может быть просто невозможно - например, a ClassLoader может генерировать данные на лету в зависимости от того, какое имя ресурса запрашивается. Если вы посмотрите на ClassLoaderAPI (который в основном работает с механизмом пути к классам), вы увидите, что делать то, что вы хотите, нечего.

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

Одна альтернатива, если файлы сначала создаются отдельно, - это включить своего рода файл манифеста, содержащий список доступных ресурсов. Объедините это в jar-файл или включите его в файловую систему как файл и загрузите его, прежде чем предлагать пользователю выбор ресурсов.


3
Я согласен, что это раздражает, но это делает ClassLoader более широко применимым в других отношениях. Например, легко написать «веб-загрузчик классов», потому что Интернет хорош для загрузки файлов, но обычно он не перечисляет файлы.
Джон Скит,

Похоже, что если ресурс, который вы пытаетесь загрузить, намного больше, чем 1 МБ, InputStreamвы получаете от getResourceAsStreamостановок для извлечения байтов ресурса после этого размера и вместо этого возвращает 0, если он содержится в сжатой файловой системе, такой как банка. В этом случае вы, кажется, вынуждены использовать 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 в пути к классам, только с файлами и каталогами
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()получится.
Olivier Faucheux

10

Надежный способ создать экземпляр File на ресурсе, полученном из jar, - это скопировать ресурс в виде потока во временный File (временный файл будет удален при выходе из 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, но не думаю, что смогу загрузить каталог из пути к классам через это.
Mantrum

В каталоге Java есть файл (я думаю, корни UNIX), так что вы должны хотя бы попробовать.
topchef

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

2

Это один из вариантов: http://www.uofr.net/~greg/java/get-resource-listing.html


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