getResourceAsStream restituisce null


183

Sto caricando un file di testo all'interno di un pacchetto in un JAR compilato del mio progetto Java. La struttura di directory pertinente è la seguente:

/src/initialization/Lifepaths.txt

Il mio codice carica un file chiamando Class::getResourceAsStreamper restituire a 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();}
}

La stampa verrà sempre stampata null, indipendentemente da ciò che uso. Non sono sicuro del motivo per cui quanto sopra non funzionerebbe, quindi ho anche provato:

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

Nessuno di questi lavori. Finora ho letto numerose domande sull'argomento, ma nessuna di queste è stata utile - di solito, dicono solo di caricare i file usando il percorso di root, che sto già facendo. Quello, o semplicemente caricare il file dalla directory corrente (basta caricare filename), che ho anche provato. Il file viene compilato nel JAR nella posizione appropriata con il nome appropriato.

Come lo risolvo?


3
Hai verificato che davvero è nel file jar? Hai controllato il case dei file?
Jon Skeet,

@JonSkeet Viene effettivamente compilato nel file JAR nella posizione appropriata e il caso è corretto.

1
@greedybuddha Anche se non posso invocarlo da un contesto statico, posso invocarlo usando Lifepaths.class. Detto questo, perché gli getClassLoader()consente di funzionare? (Inoltre, sentiti libero di inviare una risposta!)

Puoi mostrare Lifepaths.getClass()? Non esiste un metodo statico definito nell'Oggetto ...
Puce,

1
Dai un'occhiata a questa risposta e vedi se riesci a farlo funzionare usando getResource(String). A proposito, ho sempre avuto problemi a far funzionare uno di questi in un staticcontesto. Il problema è fondamentalmente che il caricatore di classi ottenuto è quello inteso per le classi J2SE. È necessario accedere al programma di caricamento della classe di contesto che è destinato all'applicazione stessa.
Andrew Thompson,

Risposte:


159

Lifepaths.class.getClass().getResourceAsStream(...) carica le risorse usando il caricatore di classi di sistema, ovviamente fallisce perché non vede i tuoi JAR

Lifepaths.class.getResourceAsStream(...) carica le risorse utilizzando lo stesso caricatore di classi che ha caricato la classe Lifepaths e dovrebbe avere accesso alle risorse nei JAR


129
Solo per aggiungere, quando si richiama getResourceAsStream (nome), il nome deve iniziare con "/". Non sono sicuro che ciò sia necessario, ma ho problemi senza.
David,

3
Ho rovinato tutto dalle 8 di questa mattina di ieri. Mi ha salvato. Avevo anche bisogno di una barra per farlo funzionare.
Kyle,

4
Inoltre, tieni presente che l'origine desiderata può essere al di fuori della gerarchia dei pacchetti. In questo caso dovrai usare "../" nel tuo percorso per salire di un livello e poi scendere su un altro ramo del percorso per raggiungere la tua risorsa.
Zon,

3
@David - Penso che (portando '/') sia necessario altrimenti cerca relativamente al pacchetto Lifepaths.class
Mz A

5
Solo per aggiungere alcune informazioni, è necessario aggiungere una /prima del percorso se il file si trova in una directory diversa; per esempio initialization/Lifepaths.txt. Se il percorso del file è lo stesso della tua classe (ma sotto le risorse come dir principale) puoi semplicemente inserire il nome del file senza nessuno /. Ad esempio, se la tua classe ha il seguente percorso src/main/java/paths/Lifepaths.java, il tuo file deve avere questo percorso src/main/resources/paths/Lifepaths.txt.
Dwhitz,

60

Le regole sono le seguenti:

  1. controlla la posizione del file che vuoi caricare all'interno del JAR (e quindi assicurati che sia effettivamente aggiunto al JAR)
  2. utilizzare un percorso assoluto: il percorso inizia dalla radice del JAR
  3. usa un percorso relativo: il percorso inizia nella directory del pacchetto della classe che stai chiamando getResource / getResoucreAsStream

E prova:

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

invece di

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

(non sono sicuro che faccia la differenza, ma il primo utilizzerà il ClassLoader / JAR corretto, mentre non sono sicuro con il secondo)


2
Ho già fatto tutte e tre queste cose. Per favore, rileggi la mia domanda.

Dalla tua domanda non è chiaro quale sia "La struttura di directory pertinente" e se hai effettivamente verificato se e dove si trova il file nel JAR (passaggio 1)
Puce,

1
La tua osservazione sul percorso relativo ha finalmente risolto il problema alla mia fine; Grazie!
Paul Bormans,

Interessante. Si scopre che ho dovuto fare "/config.properties" (con una barra) per arrivarci ...
Erk,

45

Quindi ci sono diversi modi per ottenere una risorsa da un vaso e ognuno ha una sintassi leggermente diversa in cui il percorso deve essere specificato in modo diverso.

La migliore spiegazione che ho visto è questo articolo di JavaWorld . Riassumo qui, ma se vuoi saperne di più dovresti dare un'occhiata all'articolo.

metodi

1) ClassLoader.getResourceAsStream().

Formato: "/" - nomi separati; nessun "/" iniziale (tutti i nomi sono assoluti).

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

2) Class.getResourceAsStream()

Formato: "/" - nomi separati; "/" iniziale indica nomi assoluti; tutti gli altri nomi sono relativi al pacchetto della classe

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


il tuo secondo esempio è rotto
Janus Troelsen,

1
Il secondo esempio non è rotto, come ho detto nella risposta dipende da dove si trova la risorsa.
Greedybuddha,

Inoltre: assicurati che il tuo IDE veda il file ('some / pkg / resource.properties') aggiornando la cartella di origine.
Eric Duminil,

15

Non usare percorsi assoluti, rendili relativi alla directory 'risorse' nel tuo progetto. Codice rapido e sporco che visualizza il contenuto di MyTest.txt dalla directory "risorse".

@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);
}


8

Sembra che ci sia un problema con ClassLoader che stai utilizzando. Utilizzare contextClassLoader per caricare la classe. Questo indipendentemente dal fatto che sia in un metodo statico / non statico

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


6

Mi sono trovato in un problema simile. Dal momento che sto usando Maven ho dovuto aggiornare il mio pom.xml per includere qualcosa del genere:

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

Nota il tag di risorsa lì dentro per specificare dove si trova quella cartella. Se hai progetti nidificati (come faccio io), potresti voler ottenere risorse da altre aree invece che solo nel modulo in cui stai lavorando. Ciò aiuta a ridurre il mantenimento dello stesso file in ogni repository se si utilizzano dati di configurazione simili


3

Ciò che ha funzionato per me è stato aggiungere il file in My Project/Java Resources/srce quindi utilizzare

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

Non avevo bisogno di aggiungere esplicitamente questo file al percorso (aggiungendolo per farlo /srcapparentemente)


Sintassi java errata
Ilya Kharlamov,

2

Non so se di aiuto, ma nel mio caso avevo la mia risorsa nella cartella / src / e stavo ottenendo questo errore. Ho quindi spostato l'immagine nella cartella bin e risolto il problema.


2

Assicurati che la tua directory delle risorse (es. "Src") sia nel tuo percorso di classe (assicurati che sia una directory sorgente nel tuo percorso di compilazione in eclipse).

Assicurarsi che clazz sia caricato dal classloader principale.

Quindi, per caricare src / initialization / Lifepaths.txt, utilizzare

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

Perché: clazz.getResourcesAsStream(foo)cerca all'interno del percorso di classe di clazz , rispetto alla directory in cui vive clazz . Il "/" iniziale lo fa caricare dalla radice di qualsiasi directory nel percorso di classe di clazz.

A meno che tu non sia in un contenitore di qualche tipo, come Tomcat, o non stia facendo qualcosa direttamente con ClassLoaders, puoi semplicemente considerare il tuo percorso di classe eclipse / linea di comando come l'unico percorso di classe del classloader.


2

Il programma di caricamento classe JVM predefinita utilizzerà genitore-classloader caricare le risorse prima: deletegate-genitore-classloader.

Lifepaths.class.getClass()Il classloader è bootstrap classloader, quindi getResourceAsStreamcercherà solo $ JAVA_HOME, indipendentemente dall'utente fornito classpath. Ovviamente, Lifepaths.txt non c'è.

Lifepaths.classIl classloader è system classpath classloader, quindi getResourceAsStreamcercherà definito dall'utenteclasspath e Lifepaths.txt è lì.

Durante l'utilizzo java.lang.Class#getResourceAsStream(String name), il nome che non inizia con '/' verrà aggiunto package namecome prefisso. Se si desidera evitare questo, si prega di utilizzare java.lang.ClassLoader#getResourceAsStream. Per esempio:

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

2

In parole povere:

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

Supponiamo che la struttura del progetto sia la seguente:

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

se stai usando Maven assicurati che il tuo imballaggio sia 'jar' non 'pom'.

<packaging>jar</packaging>

0

Ciò di cui hai veramente bisogno è un classPath assoluto completo per il file. Quindi, invece di indovinarlo, prova a scoprire il ROOT e quindi sposta il file in una posizione migliore in base a una struttura di file <.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

Quello che ha funzionato per me è che ho inserito il file

src/main/java/myfile.log

e

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

Si chiama la cartella di origine src/main/JAVA, ovviamente i file non di codice non dovrebbero trovarsi qui.
leyren,

-12

@Emracool ... Ti suggerirei un'alternativa. Dal momento che sembra che tu stia provando a caricare un file * .txt. Meglio usare FileInputStream()piuttosto che questo fastidiosogetClass().getClassLoader().getResourceAsStream() o getClass().getResourceAsStream(). Almeno il tuo codice verrà eseguito correttamente.


che cosa ??? -1 per tale risposta operativa. Non importa cosa. Ma la soluzione sopra suggerita funzionerà di sicuro.
Jain,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.