getResourceAsStream gibt null zurück

182

Ich lade eine Textdatei aus einem Paket in eine kompilierte JAR meines Java-Projekts. Die relevante Verzeichnisstruktur lautet wie folgt:

/src/initialization/Lifepaths.txt

Mein Code lädt eine Datei, indem er aufruft Class::getResourceAsStream, um a zurückzugeben 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();}
}

Der Ausdruck wird immer gedruckt null, egal was ich benutze. Ich bin mir nicht sicher, warum das oben genannte nicht funktionieren würde, also habe ich auch versucht:

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

Keine dieser Arbeiten. Ich habe bisher zahlreiche Fragen zu diesem Thema gelesen , aber keine davon war hilfreich - normalerweise wird nur gesagt, dass Dateien über den Stammpfad geladen werden sollen, was ich bereits tue. Das, oder laden Sie einfach die Datei aus dem aktuellen Verzeichnis (nur laden ), was ich auch versucht habe. Die Datei wird an der entsprechenden Stelle mit dem entsprechenden Namen in die JAR kompiliert. filename

Wie löse ich das?

Basil Bourque
quelle
3
Haben Sie überprüft , dass es wirklich ist in der JAR - Datei? Haben Sie das Aktengehäuse überprüft?
Jon Skeet
@ JonSkeet Es wird tatsächlich an der entsprechenden Stelle in die JAR-Datei kompiliert, und der Fall ist korrekt.
1
@greedybuddha Obwohl ich das nicht aus einem statischen Kontext heraus aufrufen kann, kann ich es mit aufrufen Lifepaths.class. Davon abgesehen, warum lässt getClassLoader()es funktionieren? (Auch
Kannst du zeigen Lifepaths.getClass()? Es gibt keine solche statische Methode in Objekt ...
Puce
1
Schauen Sie sich diese Antwort an und sehen Sie, ob Sie sie zum Laufen bringen können getResource(String). Übrigens: Ich hatte immer Probleme, einen von beiden in einem staticKontext zum Laufen zu bringen. Das Problem ist im Grunde, dass der erhaltene Klassenlader derjenige ist, der für J2SE-Klassen bestimmt ist. Sie müssen Zugriff auf die bekommen Kontext Class Loader , die für die Anwendung vorgesehen ist selbst.
Andrew Thompson

Antworten:

158

Lifepaths.class.getClass().getResourceAsStream(...) Lädt Ressourcen mit dem System Class Loader. Dies schlägt offensichtlich fehl, da Ihre JARs nicht angezeigt werden

Lifepaths.class.getResourceAsStream(...) Lädt Ressourcen mit demselben Klassenladeprogramm, das die Lifepaths-Klasse geladen hat, und sollte Zugriff auf Ressourcen in Ihren JARs haben

hoaz
quelle
127
Nur zum Hinzufügen: Beim Aufrufen von getResourceAsStream (Name) muss der Name mit "/" beginnen. Ich bin nicht sicher, ob dies notwendig ist, aber ich habe ein Problem ohne es.
David
3
Ich habe seit 8 Uhr morgens / gestern Morgen damit geschraubt. Rettete mich. Ich brauchte auch einen führenden Schrägstrich, damit es funktioniert.
Kyle
4
Beachten Sie auch, dass sich die gewünschte Quelle außerhalb der Pakethierarchie befinden kann. In diesem Fall müssen Sie "../" in Ihrem Pfad verwenden, um eine Ebene zu erreichen, und dann zu einem anderen Pfadzweig, um zu Ihrer Ressource zu gelangen.
Zon
3
@ David - Ich denke, es (führendes '/') ist notwendig, sonst sucht es relativ zum Lifepaths.class-Paket
Mz A
5
Um einige Informationen hinzuzufügen, müssen Sie /vor Ihrem Pfad ein hinzufügen, wenn sich Ihre Datei in einem anderen Verzeichnis befindet. zum Beispiel initialization/Lifepaths.txt. Wenn der Pfad der Datei das ist dieselbe von yout Klasse (aber unter Ressourcen als Haupt dir) können Sie den Namen der Datei , ohne gerade auf /. Wenn Ihre Klasse beispielsweise den folgenden Pfad hat src/main/java/paths/Lifepaths.java, muss Ihre Datei diesen Pfad haben src/main/resources/paths/Lifepaths.txt.
Dwhitz
59

Die Regeln lauten wie folgt:

  1. Überprüfen Sie den Speicherort der Datei, die Sie in die JAR laden möchten (und stellen Sie daher auch sicher, dass sie tatsächlich zur JAR hinzugefügt wurde).
  2. Verwenden Sie entweder einen absoluten Pfad: Der Pfad beginnt an der Wurzel der JAR
  3. Verwenden Sie einen relativen Pfad: Der Pfad beginnt im Paketverzeichnis der Klasse, die Sie getResource / getResoucreAsStream aufrufen

Und versuche:

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

anstatt

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

(Ich bin mir nicht sicher, ob es einen Unterschied macht, aber der erstere verwendet den richtigen ClassLoader / JAR, während ich bei dem letzteren nicht sicher bin.)

Puce
quelle
2
Ich habe bereits alle drei Dinge getan. Bitte lesen Sie meine Frage noch einmal.
Aus Ihrer Frage geht nicht hervor, was "die relevante Verzeichnisstruktur" ist und ob Sie tatsächlich überprüft haben, ob und wo sich die Datei in der JAR befindet (Schritt 1)
Puce
1
Ihre Bemerkung über den relativen Weg löste schließlich das Problem an meinem Ende; Vielen Dank!
Paul Bormans
Interessant. Es stellte sich heraus, dass ich "/config.properties" (mit einem Schrägstrich) ausführen musste, um dorthin zu gelangen ...
Erk
45

Es gibt also verschiedene Möglichkeiten, eine Ressource aus einem JAR abzurufen, und jede hat eine leicht unterschiedliche Syntax, bei der der Pfad anders angegeben werden muss.

Die beste Erklärung, die ich gesehen habe, ist dieser Artikel aus JavaWorld . Ich fasse hier zusammen, aber wenn Sie mehr wissen wollen, sollten Sie den Artikel lesen.

Methoden

1) ClassLoader.getResourceAsStream().

Format: "/" - getrennte Namen; kein führendes "/" (alle Namen sind absolut).

Beispiel: this.getClass().getClassLoader().getResourceAsStream("some/pkg/resource.properties");

2) Class.getResourceAsStream()

Format: "/" - getrennte Namen; führendes "/" zeigt absolute Namen an; Alle anderen Namen beziehen sich auf das Paket der Klasse

Beispiel: this.getClass().getResourceAsStream("/some/pkg/resource.properties");

Greedybuddha
quelle
Ihr zweites Beispiel ist kaputt
Janus Troelsen
1
Das zweite Beispiel ist nicht kaputt, wie ich in der Antwort sagte, es hängt davon ab, wo sich die Ressource befindet.
Greedybuddha
Außerdem: Stellen Sie sicher, dass Ihre IDE die Datei ('some / pkg / resource.properties') sieht, indem Sie den Quellordner aktualisieren.
Eric Duminil
15

Verwenden Sie keine absoluten Pfade, sondern machen Sie sie relativ zum Ressourcenverzeichnis in Ihrem Projekt. Schneller und schmutziger Code, der den Inhalt von MyTest.txt aus dem Verzeichnis 'resources' anzeigt.

@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
quelle
8

Es scheint ein Problem mit dem von Ihnen verwendeten ClassLoader zu geben. Verwenden Sie den contextClassLoader, um die Klasse zu laden. Dies ist unabhängig davon, ob es sich um eine statische / nicht statische Methode handelt

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

Binita Bharati
quelle
5

Ich befand mich in einem ähnlichen Problem. Da ich maven verwende, musste ich meine pom.xml so aktualisieren, dass sie Folgendes enthält:

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

Notieren Sie sich dort das Ressourcen-Tag, um anzugeben, wo sich dieser Ordner befindet. Wenn Sie verschachtelte Projekte haben (wie ich), möchten Sie möglicherweise Ressourcen aus anderen Bereichen abrufen, anstatt nur in dem Modul, in dem Sie arbeiten. Dies hilft, die Verwendung derselben Datei in jedem Repo zu reduzieren, wenn Sie ähnliche Konfigurationsdaten verwenden

plosco
quelle
3

Was für mich funktionierte, war das Hinzufügen der Datei unter My Project/Java Resources/src und dann zu verwenden

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

Ich musste diese Datei nicht explizit zum Pfad hinzufügen (das Hinzufügen zu /srcmacht das anscheinend)

CommonCoreTawan
quelle
Falsche Java-Syntax
Ilya Kharlamov
2

Ich weiß nicht, ob ich Hilfe brauche, aber in meinem Fall hatte ich meine Ressource im Ordner / src / und bekam diesen Fehler. Ich habe das Bild dann in den Ordner bin verschoben und das Problem behoben.

Leo Ufimtsev
quelle
2

Stellen Sie sicher, dass sich Ihr Ressourcenverzeichnis (z. B. "src") in Ihrem Klassenpfad befindet (stellen Sie sicher, dass es sich in Eclipse um ein Quellverzeichnis in Ihrem Erstellungspfad handelt).

Stellen Sie sicher, dass clazz vom Hauptklassenlader geladen wird.

Verwenden Sie dann zum Laden von src / initialization / Lifepaths.txt

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

Warum: clazz.getResourcesAsStream(foo)schaut foo aus dem Klassenpfad von clazz nach , relativ zu dem Verzeichnis , in dem clazz lebt . Das führende "/" bewirkt, dass es aus dem Stammverzeichnis eines beliebigen Verzeichnisses im Klassenpfad von clazz geladen wird.

Sofern Sie sich nicht in einem Container wie Tomcat befinden oder etwas direkt mit ClassLoaders tun, können Sie Ihren Eclipse- / Befehlszeilen-Klassenpfad einfach als den einzigen Classloader-Klassenpfad behandeln.

Bobby Martin
quelle
2

Der Standard-JVM-Klassenlader verwendet den übergeordneten Klassenlader, um zuerst Ressourcen zu laden: deletegate-parent-classloader .

Lifepaths.class.getClass()'s Classloader ist bootstrap classloader, also getResourceAsStreamwird nur $ JAVA_HOME durchsucht, unabhängig vom angegebenen Benutzerclasspath . Offensichtlich ist Lifepaths.txt nicht da.

Lifepaths.class's Classloader ist system classpath classloader, so getResourceAsStreamwird die Suche benutzerdefiniertclasspath und Lifepaths.txt ist da.

Bei Verwendung java.lang.Class#getResourceAsStream(String name)wird ein Name, der nicht mit '/' beginnt ,package name als Präfix hinzugefügt . Wenn Sie dies vermeiden möchten, verwenden Sie bitte java.lang.ClassLoader#getResourceAsStream. Beispielsweise:

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

Grob gesagt:

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

Angenommen, Ihre Projektstruktur sieht wie folgt aus:

├── 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
quelle
2

Wenn Sie Maven verwenden, stellen Sie sicher, dass Ihre Verpackung "jar" und nicht "pom" ist.

<packaging>jar</packaging>
Feku279
quelle
0

Was Sie wirklich brauchen, ist ein vollständiger absoluter Klassenpfad für die Datei. Versuchen Sie also, anstatt es zu erraten, den ROOT herauszufinden und verschieben Sie die Datei dann an einen besseren Ort, der auf einer <.war> -Dateistruktur basiert ...

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
quelle
0

Was für mich funktioniert hat, ist, dass ich die Datei unter platziert habe

src/main/java/myfile.log

und

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

@ Emracool ... Ich würde Ihnen eine Alternative vorschlagen. Da scheinen Sie zu versuchen, eine * .txt-Datei zu laden. Besser zu verwenden als FileInputStream()dieses nervige getClass().getClassLoader().getResourceAsStream()oder getClass().getResourceAsStream(). Zumindest wird Ihr Code ordnungsgemäß ausgeführt.

Jain
quelle
was ??? -1 für eine solche funktionierende Antwort. Egal was. Aber die oben vorgeschlagene Lösung wird sicher funktionieren.
Jain