getResourceAsStream zwraca null

183

Ładuję plik tekstowy z pakietu w skompilowanym pliku JAR mojego projektu Java. Odpowiednia struktura katalogów jest następująca:

/src/initialization/Lifepaths.txt

Mój kod ładuje plik, wywołując Class::getResourceAsStreama 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();}
}

Wydruk zawsze będzie drukowany null, bez względu na to, z czego korzystam. Nie jestem pewien, dlaczego powyższe nie działa, więc próbowałem też:

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

Żadna z tych prac. Do tej pory przeczytałem wiele pytań na ten temat, ale żadne z nich nie było pomocne - zwykle mówią tylko, aby ładować pliki przy użyciu ścieżki katalogu głównego, co już robię. To lub po prostu załaduj plik z bieżącego katalogu (po prostu załaduj filename), co również próbowałem. Plik jest kompilowany do pliku JAR w odpowiednim miejscu o odpowiedniej nazwie.

Jak to rozwiązać?

Basil Bourque
źródło
3
Czy sprawdziłeś, że tak naprawdę jest w pliku jar? Czy sprawdziłeś obudowę pliku?
Jon Skeet
@JonSkeet Rzeczywiście jest on kompilowany do pliku JAR w odpowiednim miejscu i sprawa jest poprawna.
1
@greedybuddha Chociaż nie mogę wywoływać tego ze statycznego kontekstu, mogę go wywoływać za pomocą Lifepaths.class. Biorąc to pod uwagę, dlaczego getClassLoader()pozwala to działać? (Napisz też odpowiedź!)
Można pokazać Lifepaths.getClass()? Nie ma takiej statycznej metody zdefiniowanej w Object ...
Puce
1
Spójrz na tę odpowiedź i sprawdź, czy możesz ją uruchomić getResource(String). BTW - Zawsze miałem problemy z nakłonieniem któregokolwiek z nich do pracy w statickontekście. Problem polega na tym, że uzyskany moduł ładujący jest przeznaczony dla klas J2SE. Musisz uzyskać dostęp do modułu ładującego klasy kontekstu, który jest przeznaczony dla samej aplikacji.
Andrew Thompson,

Odpowiedzi:

159

Lifepaths.class.getClass().getResourceAsStream(...) ładuje zasoby za pomocą modułu ładującego klasy systemowe, oczywiście kończy się niepowodzeniem, ponieważ nie widzi plików JAR

Lifepaths.class.getResourceAsStream(...) ładuje zasoby przy użyciu tego samego modułu ładującego, który załadował klasę Lifepaths i powinien mieć dostęp do zasobów w twoich plikach JAR

hoaz
źródło
129
Aby dodać, przy wywoływaniu getResourceAsStream (name) nazwa musi zaczynać się od „/”. Nie jestem pewien, czy jest to konieczne, ale mam problem bez tego.
David
3
Spieprzyłem to od 8 rano tego / wczorajszego ranka. Uratował mnie Potrzebowałem też wiodącego slasha, żeby działał.
kyle
4
Należy również pamiętać, że żądane źródło może znajdować się poza hierarchią pakietów. W takim przypadku musisz użyć „../” na swojej ścieżce, aby przejść o jeden poziom, a następnie zejść na inną gałąź ścieżki, aby dotrzeć do zasobu.
Strefa
3
@David - Myślę, że to (wiodące '/') jest konieczne, w przeciwnym razie przeszukuje pakiet Lifepaths.class
Mz A
5
Aby dodać jakieś informacje, musisz dodać /przed ścieżką, jeśli plik znajduje się w innym katalogu; na przykład initialization/Lifepaths.txt. Jeśli ścieżka do pliku jest taka sama jak klasa yout (ale w zasobach jak główny katalog), możesz po prostu umieścić nazwę pliku bez żadnej /. Na przykład, jeśli twoja klasa ma następującą ścieżkę src/main/java/paths/Lifepaths.java, twój plik musi mieć tę ścieżkę src/main/resources/paths/Lifepaths.txt.
Dwhitz,
59

Reguły są następujące:

  1. sprawdź lokalizację pliku, który chcesz załadować w pliku JAR (a tym samym upewnij się, że rzeczywiście został dodany do pliku JAR)
  2. użyj albo ścieżki bezwzględnej: ścieżka zaczyna się w katalogu głównym pliku JAR
  3. użyj ścieżki względnej: ścieżka zaczyna się od katalogu pakietu klasy, którą nazywasz getResource / getResoucreAsStream

I próbuj:

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

zamiast

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

(nie jestem pewien, czy to robi różnicę, ale ten pierwszy użyje poprawnego ClassLoadera / JAR, podczas gdy nie jestem pewien co do tego drugiego)

Kolor brązowofioletowy
źródło
2
Zrobiłem już wszystkie te trzy rzeczy. Proszę ponownie przeczytać moje pytanie.
Z twojego pytania nie jest jasne, co to jest „odpowiednia struktura katalogów” i czy rzeczywiście sprawdziłeś, czy i gdzie plik znajduje się w JAR (krok 1)
Puce
1
Twoja uwaga na temat ścieżki względnej ostatecznie rozwiązała problem na moim końcu; dzięki!
Paul Bormans
Ciekawy. Okazuje się, że musiałem zrobić „/config.properties” (z ukośnikiem), aby się do niego dostać…
Erk
45

Istnieje więc kilka sposobów na uzyskanie zasobu ze słoika, a każdy z nich ma nieco inną składnię, w której ścieżka musi być określona inaczej.

Najlepszym wyjaśnieniem, jakie widziałem, jest ten artykuł z JavaWorld . Podsumuję tutaj, ale jeśli chcesz dowiedzieć się więcej, zapoznaj się z tym artykułem.

Metody

1) ClassLoader.getResourceAsStream().

Format: „/” - rozdzielone nazwy; bez wiodących znaków „/” (wszystkie nazwy są bezwzględne).

Przykład: this.getClass().getClassLoader().getResourceAsStream("some/pkg/resource.properties");

2) Class.getResourceAsStream()

Format: „/” - rozdzielone nazwy; wiodące „/” oznacza nazwy bezwzględne; wszystkie inne nazwy odnoszą się do pakietu klasy

Przykład: this.getClass().getResourceAsStream("/some/pkg/resource.properties");

greedybuddha
źródło
twój drugi przykład jest zepsuty
Janus Troelsen
1
Drugi przykład nie jest zepsuty, jak powiedziałem w odpowiedzi, zależy to od tego, gdzie znajduje się zasób.
greedybuddha,
Ponadto: upewnij się, że IDE widzi plik („some / pkg / resource.properties”), odświeżając folder źródłowy.
Eric Duminil
15

Nie używaj ścieżek bezwzględnych, uczyń je względnymi do katalogu „resources” w swoim projekcie. Szybki i brudny kod wyświetlający zawartość MyTest.txt z katalogu „resources”.

@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);
}
Paul Smith
źródło
8

Wygląda na to, że występuje problem z używanym programem ClassLoader. Użyj kontekstuClassLoader, aby załadować klasę. Jest to niezależne od tego, czy jest to metoda statyczna / niestatyczna

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

Binita Bharati
źródło
5

Znalazłem się w podobnym problemie. Ponieważ używam maven, musiałem zaktualizować plik pom.xml, aby zawierał coś takiego:

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

Zanotuj tam znacznik zasobów, aby określić, gdzie znajduje się ten folder. Jeśli zagnieżdżałeś projekty (tak jak ja), możesz chcieć uzyskać zasoby z innych obszarów zamiast tylko w module, w którym pracujesz. Pomaga to ograniczyć utrzymywanie tego samego pliku w każdym repozytorium, jeśli używasz podobnych danych konfiguracyjnych

Plosco
źródło
3

Dla mnie zadziałało dodanie pliku, My Project/Java Resources/srca następnie użycie

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

Nie musiałem jawnie dodawać tego pliku do ścieżki (dodanie go /srcnajwyraźniej robi to)

CommonCoreTawan
źródło
Zła składnia java
Ilya Kharlamov
2

Nie wiem czy pomogę, ale w moim przypadku miałem swój zasób w folderze / src / i otrzymywałem ten błąd. Następnie przeniosłem zdjęcie do folderu bin i to rozwiązało problem.

Lew Ufimcew
źródło
2

Upewnij się, że katalog zasobów (np. „Src”) znajduje się w ścieżce klasy (upewnij się, że jest to katalog źródłowy na ścieżce kompilacji w środowisku Eclipse).

Upewnij się, że clazz jest ładowany z głównego modułu ładującego klasy.

Następnie, aby załadować src / initialization / Lifepaths.txt, użyj

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

Dlaczego: clazz.getResourcesAsStream(foo)wyszukuje foo ze ścieżki klasy clazz w stosunku do katalogu, w którym mieszka clazz . Wiodące „/” powoduje, że ładuje się z katalogu głównego dowolnego katalogu w ścieżce klas clazz.

O ile nie jesteś w jakimś kontenerze, takim jak Tomcat, lub nie robisz czegoś bezpośrednio z ClassLoaders, możesz po prostu traktować swoją ścieżkę klas zaćmienia / linii poleceń jako jedyną ścieżkę klasy programu ładującego klasy.

Bobby Martin
źródło
2

Domyślną JVM ClassLoader użyje rodzic-classloader załadować zasobów pierwszy: deletegate-parent-classloader.

Lifepaths.class.getClass()Classloader jest bootstrap classloader, więc getResourceAsStreamprzeszukuje tylko $ JAVA_HOME, niezależnie od podanego użytkownika classpath. Oczywiście Lifepaths.txt nie ma.

Lifepaths.classClassloader jest system classpath classloader, więc getResourceAsStreambędzie wyszukiwał zdefiniowane przez użytkownika classpathi Lifepaths.txt tam jest.

Podczas używania java.lang.Class#getResourceAsStream(String name)nazwa, która nie zaczyna się od „/”, zostanie dodana package namejako prefiks. Jeśli chcesz tego uniknąć, skorzystaj z java.lang.ClassLoader#getResourceAsStream. Na przykład:

ClassLoader loader = Thread.currentThread().getContextClassLoader();
String resourceName = "Lifepaths.txt";
InputStream resourceStream = loader.getResourceAsStream(resourceName); 
ridox
źródło
2

Z grubsza mówiąc:

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

Załóżmy, że struktura projektu wygląda następująco:

├── 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
Shijing Lv
źródło
2

jeśli używasz Maven, upewnij się, że twoje opakowanie to „jar”, ​​a nie „pom”.

<packaging>jar</packaging>
Feku279
źródło
0

To, czego naprawdę potrzebujesz, to pełna bezwzględna ścieżka klasy dla pliku. Zamiast zgadywać, spróbuj znaleźć ROOT, a następnie przenieś plik do lepszej lokalizacji, bazowej struktury plików <.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());
ConAim
źródło
0

Dla mnie zadziałało, że umieściłem plik

src/main/java/myfile.log

i

InputStream is = getClass().getClassLoader().getResourceAsStream("myfile.log");
        
        if (is == null) {
            throw new FileNotFoundException("Log file not provided");
        }
Akash Yellappa
źródło
Nazywa się folder źródłowy src/main/JAVA, oczywiście nie należy tutaj umieszczać plików innych niż kodowe.
leyren
-12

@Emracool ... Proponuję ci alternatywę. Ponieważ wydaje się, że próbujesz załadować plik * .txt. Lepiej używać FileInputStream()raczej niż tego denerwującego getClass().getClassLoader().getResourceAsStream()lub getClass().getResourceAsStream(). Przynajmniej twój kod wykona się poprawnie.

Jain
źródło
co ??? -1 za taką roboczą odpowiedź. Nieważne co. Ale powyżej sugerowane rozwiązanie na pewno zadziała.
Jain