getResourceAsStream повертає null


183

Я завантажую текстовий файл із пакету в складений JAR мого проекту Java. Відповідна структура каталогу така:

/src/initialization/Lifepaths.txt

Мій код завантажує файл, викликаючи Class::getResourceAsStreamповернення InputStream.

public class Lifepaths {
    public static void execute() {
        System.out.println(Lifepaths.class.getClass().
            getResourceAsStream("/initialization/Lifepaths.txt"));
    }

    private Lifepaths() {}

    //This is temporary; will eventually be called from outside
    public static void main(String[] args) {execute();}
}

Друк завжди буде надрукованим null, незалежно від того, чим я користуюся. Я не впевнений, чому вищезгадане не вийде, тому я також спробував:

  • "/src/initialization/Lifepaths.txt"
  • "initialization/Lifepaths.txt"
  • "Lifepaths.txt"

Жодне з цих не працює. Я читав численні запитання на цю тему, але жодне з них не було корисним - зазвичай вони просто кажуть завантажувати файли за допомогою кореневого шляху, що я вже роблю. Це, або просто завантажуйте файл із поточного каталогу (просто завантажуйте filename), що я також намагався. Файл збирається в JAR у відповідному місці з відповідним іменем.

Як я це вирішую?


3
Ви перевірили, чи справді він є у файлі jar? Ви перевірили корпус файлу?
Джон Скіт

@JonSkeet Дійсно збирається у файл JAR у відповідному місці, і справа правильна.

1
@greedybuddha Хоча я не можу викликати це зі статичного контексту, я можу викликати це за допомогою Lifepaths.class. Якщо говорити, чому це getClassLoader()дозволяє йому працювати? (Також

Ви можете показати Lifepaths.getClass()? Не існує такого статичного методу, визначеного в Object ...
Пуч

1
Погляньте на цю відповідь і подивіться, чи зможете ви її працювати getResource(String). BTW - У мене завжди були проблеми з тим, щоб працювати з будь-яким із них у staticконтексті. Проблема полягає в тому, що отриманий завантажувач класів - це той, який призначений для класів J2SE. Вам потрібно отримати доступ до завантажувача контекстного класу, який призначений для самої програми.
Ендрю Томпсон

Відповіді:


159

Lifepaths.class.getClass().getResourceAsStream(...) завантажує ресурси за допомогою завантажувача системного класу, це, очевидно, виходить з ладу, оскільки він не бачить ваші JAR

Lifepaths.class.getResourceAsStream(...) завантажує ресурси за допомогою того ж навантажувача класу, що завантажив клас Lifepaths, і він повинен мати доступ до ресурсів у своїх JAR


129
Просто для додавання: При виклику getResourceAsStream (ім'я) ім'я повинно починатися з "/". Я не впевнений, чи потрібно це, але у мене проблеми без цього.
Девід

3
Я з цим вкручуюсь з 8 ранку / вчора вранці. Врятувало мене. Мені також знадобився провідна коса риса, щоб вона працювала.
кайл

4
Також майте на увазі, що бажане джерело може знаходитися поза ієрархією пакетів. У цьому випадку вам доведеться використовувати "../" у своєму шляху, щоб піднятися на один рівень, а потім спуститися до іншої гілки шляху, щоб досягти вашого ресурсу.
Зона

3
@David - Я думаю, що це (ведуче '/') необхідно, інакше він шукає відносно пакету Lifepaths.class
Mz A

5
Просто для додання деякої інформації вам потрібно додати /до свого шляху, якщо ваш файл знаходиться в іншому каталозі; наприклад initialization/Lifepaths.txt. Якщо шлях до файлу той самий клас yout (але за ресурсами як основний dir), ви можете просто поставити ім’я файлу без жодного /. Наприклад, якщо ваш клас має такий шлях src/main/java/paths/Lifepaths.java, ваш файл повинен мати цей шлях src/main/resources/paths/Lifepaths.txt.
Dwhitz

59

Правила такі:

  1. перевірте розташування файлу, який ви хочете завантажити всередині JAR (і таким чином переконайтесь, що він фактично доданий до JAR)
  2. використовувати або абсолютний шлях: шлях починається в корені JAR
  3. використовувати відносний шлях: шлях починається в каталозі пакетів класу, який ви викликаєте getResource / getResoucreAsStream

І спробуйте:

Lifepaths.class.getResourceAsStream("/initialization/Lifepaths.txt")

замість

Lifepaths.class.getClass().getResourceAsStream("/initialization/Lifepaths.txt")

(не впевнений, чи це має значення, але перший буде використовувати правильний ClassLoader / JAR, тоді як я не впевнений у останньому)


2
Я вже зробив усі три ці речі. Прочитайте, будь ласка, моє запитання.

З вашого запитання незрозуміло, що таке "відповідна структура каталогів" і чи ви насправді перевірили, чи і де знаходиться файл у JAR (крок 1)
Пуч

1
Ваше зауваження щодо відносного шляху остаточно вирішило питання в моєму кінці; Дякую!
Пол Борманс

Цікаво. Виявляється, мені довелося зробити "/config.properties" (з косою рисою), щоб дістатися до нього ...
Ерк

45

Отже, існує кілька способів отримати ресурс з банки, і кожен має дещо інший синтаксис, де шлях потрібно вказати по-різному.

Найкраще пояснення, яке я бачив, - це стаття від JavaWorld . Я підсумую тут, але якщо ви хочете дізнатися більше, слід ознайомитися зі статтею.

Методи

1) ClassLoader.getResourceAsStream().

Формат: "/" - відокремлені імена; немає провідних "/" (усі назви абсолютні).

Приклад: this.getClass().getClassLoader().getResourceAsStream("some/pkg/resource.properties");

2) Class.getResourceAsStream()

Формат: "/" - відокремлені імена; провідний "/" вказує абсолютні назви; всі інші назви відносяться до пакету класу

Приклад: this.getClass().getResourceAsStream("/some/pkg/resource.properties");


ваш другий приклад зламаний
Янус Трольсен

1
Другий приклад не порушений, як я вже говорив у відповіді, це залежить від того, де знаходиться ресурс.
greedybuddha

Також переконайтесь, що ваш IDE бачить файл ('some / pkg / resource.properties'), оновивши вихідну папку.
Ерік Думініл

15

Не використовуйте абсолютних контурів, а посилайтеся на каталог ресурсів у вашому проекті. Швидкий і брудний код, який відображає вміст MyTest.txt з каталогу "ресурси".

@Test
public void testDefaultResource() {
    // can we see default resources
    BufferedInputStream result = (BufferedInputStream) 
         Config.class.getClassLoader().getResourceAsStream("MyTest.txt");
    byte [] b = new byte[256];
    int val = 0;
    String txt = null;
    do {
        try {
            val = result.read(b);
            if (val > 0) {
                txt += new String(b, 0, val);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } 
    } while (val > -1);
    System.out.println(txt);
}

9

Ви можете спробувати це, щоб отримати потік, тобто спочатку отримати URL-адресу, а потім відкрити його як потік.

URL url = getClass().getResource("/initialization/Lifepaths.txt"); 
InputStream strm = url.openStream(); 

У мене колись було подібне питання: Читання файлу txt з jar не вдається, але читання зображення працює


3
саме це робить getResourceAsStream ()
федерація

8

Здається, виникла проблема з ClassLoader, який ви використовуєте. Використовуйте contextClassLoader для завантаження класу. Це незалежно від того, чи є він у статичному / нестатичному методі

Thread.currentThread().getContextClassLoader().getResourceAsStream......


5

Я опинився в подібному питанні. Оскільки я використовую maven, мені потрібно було оновити свій pom.xml, щоб включити щось подібне:

   ...
</dependencies>
<build>
    <resources>
        <resource>
            <directory>/src/main/resources</directory>
        </resource>
        <resource>
            <directory>../src/main/resources</directory>
        </resource>
    </resources>
    <pluginManagement>
        ...

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


3

Для мене працювало те, щоб додати файл під My Project/Java Resources/srcі потім використовувати

this.getClass().getClassLoader().getResourceAsStream("myfile.txt");

Мені не потрібно було явно додавати цей файл у шлях (додаючи його до /srcцього, мабуть)


Неправильний синтаксис java
Ілля Харламов

2

Не знаю, чи допоможе, але в моєму випадку я мав свій ресурс у папці / src / і отримував цю помилку. Потім я перемістив зображення в папку бін, і це вирішило проблему.


2

Переконайтеся, що ваш каталог ресурсів (наприклад, "src") знаходиться у вашому класі (переконайтеся, що це вихідний каталог у вашому шляху збирання у затемненні).

Переконайтесь, що clazz завантажений з основного завантажувача.

Потім, щоб завантажити src / ініціалізацію / Lifepaths.txt, використовуйте

clazz.getResourceAsStream("/initialization/Lifepaths.txt");

Чому: clazz.getResourcesAsStream(foo)шукає foo зсередини classzzath clazz , відносно каталогу, в якому живе клазз . Провідний "/" змушує завантажувати його з кореня будь-якого каталогу на classpath clazz.

Якщо ви не знаходитесь в контейнері такого типу, як Tomcat, або не робите щось з ClassLoaders безпосередньо, ви можете просто ставитися до класу "eclipse / command line" як до єдиного класу завантажувача.


2

JVM Завантажувач класів по замовчуванням буде використовувати батьківський-завантажувач класів для першого завантаження ресурсів: deletegate-parent-classloader.

Lifepaths.class.getClass()Завантажувач класів є bootstrap classloader, тому getResourceAsStreamбуде шукати лише $ JAVA_HOME, незалежно від наданих користувачем classpath. Очевидно, що Lifepaths.txt там немає.

Lifepaths.class's classloader є system classpath classloader, тому getResourceAsStreamбуде шукати визначені користувачем classpathі Lifepaths.txt є.

Під час використання java.lang.Class#getResourceAsStream(String name)ім'я, яке не починається з '/', буде додано package nameяк префікс. Якщо ви хочете цього уникнути, будь ласка, використовуйте java.lang.ClassLoader#getResourceAsStream. Наприклад:

ClassLoader loader = Thread.currentThread().getContextClassLoader();
String resourceName = "Lifepaths.txt";
InputStream resourceStream = loader.getResourceAsStream(resourceName); 

2

Грубо кажучи:

getClass().getResource("/") ~ = Thread.currentThread().getContextClassLoader().getResource(".")

Припустимо, ваша структура проекту така:

├── src
   ├── main
   └── test
   └── test
       ├── java
          └── com
              └── github
                  └── xyz
                      └── proj
                          ├── MainTest.java
                          └── TestBase.java
       └── resources
           └── abcd.txt
└── target
    └── test-classes
        ├── com
        └── abcd.txt
// in MainClass.java
this.getClass.getResource("/") -> "~/proj_dir/target/test-classes/"
this.getClass.getResource(".") -> "~/proj_dir/target/test-classes/com/github/xyz/proj/"
Thread.currentThread().getContextClassLoader().getResources(".") -> "~/proj_dir/target/test-classes/"
Thread.currentThread().getContextClassLoader().getResources("/") ->  null

2

якщо ви використовуєте Maven, переконайтесь, що ваша упаковка "jar", а не "pom".

<packaging>jar</packaging>

0

Те, що вам справді потрібно, - це повний абсолютний classPath для файлу. Тому замість того, щоб здогадуватися, спробуйте знайти ROOT, а потім перемістіть файл у кращу базу розташування <<.war> файлової структури ...

URL test1 = getClass().getResource("/");
URL test2 = getClass().getClassLoader().getResource("/");            
URL test3 = getClass().getClassLoader().getResource("../");

logger.info(test1.getPath()); 
logger.info(test2.getPath());
logger.info(test3.getPath());

0

Що для мене спрацювало - це я помістив файл

src/main/java/myfile.log

і

InputStream is = getClass().getClassLoader().getResourceAsStream("myfile.log");
        
        if (is == null) {
            throw new FileNotFoundException("Log file not provided");
        }

Викликається папка вихідного коду src/main/JAVA, очевидно, що ваші некодовні файли тут не повинні знаходитися.
лейрен

-12

@Emracool ... Я б запропонував вам альтернативу. Оскільки ви, схоже, намагаєтеся завантажити * .txt файл. Краще використовувати FileInputStream(), ніж це дратує getClass().getClassLoader().getResourceAsStream()або getClass().getResourceAsStream(). Принаймні ваш код буде виконано належним чином.


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