Почему getResourceAsStream возвращает значение null?

У меня есть изображение внутри банки, которое я пытаюсь загрузить, но getResourceAsStream() всегда возвращает значение null.

Структура каталога:

com/thesimpzone/watch_pugs/watch_pugs/{all my class files}
META-INF/MANIFEST.MF
resources/generic/mouse.png

Контент.java:

public abstract class Content {

    protected Map<String, BufferedImage> images = new HashMap<String, BufferedImage>();

    protected String prefix;

    public Content(String prefix){
        this.prefix = prefix;
    }

    protected void loadImage(String name){
        System.out.println(name);
        System.out.println(prefix);
        String path = (prefix + name);
        System.out.println(path);
        String identifier = name.substring(0, name.lastIndexOf("."));
        try{
            InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
            images.put(identifier, ImageIO.read(in));
        }catch(IOException | ClassCastException e){
            throw new RuntimeException("Image " + identifier + " at " + path + " could not be loaded.");
        }
    }
    [...]
}

Общий контент.java:

public class GenericContent extends Content {

    public GenericContent(){
        super("resources/generic/");
        this.loadContent();
    }

    @Override
    public void loadContent() {
        loadImage("mouse.png");
    }

}

Трассировки стека:

mouse.png
resources/generic/
resources/generic/mouse.png
Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: input == null!
    at javax.imageio.ImageIO.read(ImageIO.java:1348)
    at com.thesimpzone.watch_pugs.watch_pugs.content.Content.loadImage(Content.java:29)
    at com.thesimpzone.watch_pugs.watch_pugs.content.GenericContent.loadContent(GenericContent.java:17)
    at com.thesimpzone.watch_pugs.watch_pugs.content.GenericContent.<init>(GenericContent.java:12)
    at com.thesimpzone.watch_pugs.watch_pugs.Canvas.<init>(Canvas.java:45)
    at com.thesimpzone.watch_pugs.watch_pugs.Framework.<init>(Framework.java:75)
    at com.thesimpzone.watch_pugs.watch_pugs.Window.<init>(Window.java:50)
    at com.thesimpzone.watch_pugs.watch_pugs.Window.<init>(Window.java:26)
    at com.thesimpzone.watch_pugs.watch_pugs.Window$1.run(Window.java:60)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:744)
    at java.awt.EventQueue.access$400(EventQueue.java:97)
    at java.awt.EventQueue$3.run(EventQueue.java:697)
    at java.awt.EventQueue$3.run(EventQueue.java:691)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:714)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

Я понятия не имею, почему загрузчик классов не может найти изображение. Я посмотрел в скомпилированную банку, и файл там, и он отлично открывается в краске, так что с файлом все в порядке. Я пробовал различные варианты ClassLoader, включая getSystemClassLoader(), getClassLoader() и Content.class.getClassLoader(); а также getResourceAsStream(путь) вместо getResource(путь).openStream(). Я пробовал с префиксом «/» в начале и без него, поэтому у меня нет идей, и Google не помогает. Кроме того, кажется, что то, что я делаю, чтобы определить «префикс», очень неудобно, и поэтому, если есть лучший способ, я был бы рад, если бы кто-нибудь показал мне, как это сделать.

Спасибо.


person Oscar S    schedule 12.10.2014    source источник
comment
Примечание: prefix уже является String. Нет смысла вызывать toString() для него.   -  person Sotirios Delimanolis    schedule 12.10.2014
comment
Ах да, это было, когда я пытался использовать char[], так что это могло быть окончательным.   -  person Oscar S    schedule 12.10.2014
comment
В качестве предложения в следующий раз не редактируйте свой вопрос, переопределяя исходный код. Это искажает ответы. Если вам нужно, отредактируйте, добавив новый код, который вы пытаетесь использовать, и результаты.   -  person Sotirios Delimanolis    schedule 12.10.2014


Ответы (2)


Все дело в относительных и абсолютных пакетах при вызове getResourceAsStream(), вы ищете что-то относительно любого пакета Content в качестве корня.

в пути к классам нет «каталогов», особенно внутри файлов .jar, только пакеты

Лучше всего использовать Thread.currentThread().getContextClassloader().getResourceAsStream() с полным пакетом без начального /.

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

В твоем случае:

Thread.currentThread().getContextClassloader().getResourceAsStream("resources/generic/mouse.png");

Если вы по-прежнему получаете сообщение об ошибке при таком подходе, ваш .jar построен не так, как вы думаете, или если вы получаете это из IDE, вы, вероятно, не копируете содержимое resource/generic/ в путь к классам.

Лично я всегда использую форму:

Thread.currentThread().getContextClassLoader().getResourceAsStream("path/to/resource/file.ext");, так как он всегда работает, откуда бы вы его ни вызывали, и в каком бы загрузчике классов вы ни находились, и он явно указывает, где он ищет, чтобы найти то, что ищет.

person Community    schedule 12.10.2014
comment
Хм, все еще дает ту же самую ошибку (обновлен OP, чтобы показать текущий код). Я также пытался использовать литерал, а не переменную, но он по-прежнему показывает то же самое IllegalArgumentException из-за нулевого InputStream. - person Oscar S; 12.10.2014
comment
Тогда ваш .jar построен не так, как вы думаете, если вы получаете это из IDE, вы, вероятно, не копируете resource/generic в путь к классам. Тогда это проблема конфигурации с вашей стороны. - person ; 12.10.2014
comment
Моя структура каталогов взята из извлечения экспортированного .jar, поэтому изображение есть. - person Oscar S; 12.10.2014
comment
Хм, хорошо, я получаю сообщение об ошибке в eclipse, но, по-видимому, оно отлично загружается, когда я сейчас запускаю экспортированную (автономную) банку? - person Oscar S; 12.10.2014

Попробуйте с Content.class.getClassLoader().getResourceAsStream(path);

person SMilos    schedule 24.02.2021