getResourceAsStream renvoie null

183

Je charge un fichier texte à partir d'un package dans un JAR compilé de mon projet Java. La structure de répertoire pertinente est la suivante:

/src/initialization/Lifepaths.txt

Mon code charge un fichier en appelant Class::getResourceAsStreampour retourner un fichier 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();}
}

L'impression sera toujours imprimée null, peu importe ce que j'utilise. Je ne sais pas pourquoi ce qui précède ne fonctionnerait pas, alors j'ai également essayé:

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

Aucun de ces travaux. J'ai lu de nombreuses questions sur le sujet jusqu'à présent, mais aucune d'entre elles n'a été utile - généralement, ils disent simplement de charger les fichiers en utilisant le chemin racine, ce que je fais déjà. Cela, ou simplement charger le fichier à partir du répertoire actuel (juste charger filename), que j'ai également essayé. Le fichier est en cours de compilation dans le JAR à l'emplacement approprié avec le nom approprié.

Comment résoudre ce problème?

Basil Bourque
la source
3
Avez-vous vérifié qu'il se trouve vraiment dans le fichier jar? Avez-vous vérifié le boîtier du fichier?
Jon Skeet
@JonSkeet Il est en effet en cours de compilation dans le fichier JAR à l'emplacement approprié, et la casse est correcte.
1
@greedybuddha Bien que je ne puisse pas l'invoquer à partir d'un contexte statique, je peux l'invoquer en utilisant Lifepaths.class. Cela étant dit, pourquoigetClassLoader() permet-il de fonctionner? (Aussi, n'hésitez pas à poster une réponse!)
Pouvez-vous montrer Lifepaths.getClass()? Il n'y a pas de méthode statique de ce type définie dans Object ...
Puce
1
Jetez un œil à cette réponse et voyez si vous pouvez la faire fonctionner en utilisant getResource(String). BTW - J'ai toujours eu des problèmes pour faire fonctionner l'un ou l'autre dans un staticcontexte. Le problème est fondamentalement que le chargeur de classe obtenu est celui destiné aux classes J2SE. Vous devez avoir accès au chargeur de classe de contexte qui est destiné à l'application elle-même.
Andrew Thompson

Réponses:

159

Lifepaths.class.getClass().getResourceAsStream(...) charge les ressources à l'aide du chargeur de classe système, il échoue évidemment car il ne voit pas vos JAR

Lifepaths.class.getResourceAsStream(...) charge les ressources en utilisant le même chargeur de classe que celui qui a chargé la classe Lifepaths et il doit avoir accès aux ressources de vos JAR

hoaz
la source
129
Juste pour ajouter, lors de l'appel de getResourceAsStream (nom), le nom doit commencer par "/". Je ne sais pas si cela est nécessaire, mais j'ai un problème sans cela.
David
3
Je suis foutu avec ça depuis 8h du matin ce / hier matin. M'a sauvé. J'avais également besoin d'une barre oblique principale pour que cela fonctionne.
kyle
4
Gardez également à l'esprit que la source souhaitée peut être en dehors de la hiérarchie des packages. Dans ce cas, vous devrez utiliser "../" dans votre chemin pour monter d'un niveau puis redescendre vers une autre branche de chemin pour atteindre votre ressource.
Zon
3
@David - Je pense que (en tête '/') est nécessaire sinon il recherche par rapport au package Lifepaths.class
Mz A
5
Juste pour ajouter quelques informations, vous devez ajouter un /avant votre chemin si votre fichier se trouve dans un répertoire différent; par exemple initialization/Lifepaths.txt. Si le chemin du fichier est le même que celui de votre classe (mais sous ressources comme répertoire principal), vous pouvez simplement mettre le nom du fichier sans aucun /. Par exemple, si votre classe a le chemin suivant src/main/java/paths/Lifepaths.java, votre fichier doit avoir ce chemin src/main/resources/paths/Lifepaths.txt.
Dwhitz
60

Les règles sont les suivantes:

  1. vérifiez l'emplacement du fichier que vous souhaitez charger dans le JAR (et assurez-vous ainsi qu'il est effectivement ajouté au JAR)
  2. utilisez soit un chemin absolu: le chemin commence à la racine du JAR
  3. utilisez un chemin relatif: le chemin commence au répertoire du package de la classe que vous appelez getResource / getResoucreAsStream

Et essaye:

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

au lieu de

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

(je ne sais pas si cela fait une différence, mais le premier utilisera le bon ClassLoader / JAR, alors que je ne suis pas sûr du dernier)

Puce
la source
2
J'ai déjà fait ces trois choses. Veuillez relire ma question.
D'après votre question, on ne sait pas ce qu'est "La structure de répertoire pertinente" et si vous avez réellement vérifié si et où se trouve le fichier dans le JAR (étape 1)
Puce
1
Votre remarque sur le chemin relatif a finalement résolu le problème de mon côté; Merci!
Paul Bormans
Intéressant. Il s'avère que j'ai dû faire "/config.properties" (avec une barre oblique) pour y accéder ...
Erk
45

Il existe donc plusieurs façons d'obtenir une ressource à partir d'un fichier jar et chacune a une syntaxe légèrement différente où le chemin doit être spécifié différemment.

La meilleure explication que j'ai vue est cet article de JavaWorld . Je vais résumer ici, mais si vous voulez en savoir plus, vous devriez consulter l'article.

Méthodes

1) ClassLoader.getResourceAsStream() .

Format: "/" - noms séparés; pas de "/" de tête (tous les noms sont absolus).

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

2) Class.getResourceAsStream()

Format: "/" - noms séparés; "/" en tête indique des noms absolus; tous les autres noms sont relatifs au package de la classe

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

gourmand
la source
votre deuxième exemple est cassé
Janus Troelsen
1
Le deuxième exemple n'est pas cassé, comme je l'ai dit dans la réponse, cela dépend de l'emplacement de la ressource.
greedybuddha
Aussi: assurez-vous que votre IDE voit le fichier ('some / pkg / resource.properties') en actualisant le dossier source.
Eric Duminil
15

N'utilisez pas de chemins absolus, rendez-les relatifs au répertoire 'resources' de votre projet. Code rapide et sale qui affiche le contenu de MyTest.txt à partir du répertoire '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
la source
8

Il semble y avoir un problème avec le ClassLoader que vous utilisez. Utilisez le contextClassLoader pour charger la classe. Ceci indépendamment du fait que ce soit dans une méthode statique / non statique

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

Binita Bharati
la source
6

Je me suis retrouvé dans un problème similaire. Depuis que j'utilise maven, j'ai dû mettre à jour mon pom.xml pour inclure quelque chose comme ceci:

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

Notez la balise de ressource pour spécifier où se trouve ce dossier. Si vous avez des projets imbriqués (comme moi), vous voudrez peut-être obtenir des ressources d'autres domaines plutôt que simplement dans le module dans lequel vous travaillez. Cela permet de réduire la conservation du même fichier dans chaque dépôt si vous utilisez des données de configuration similaires

Plosco
la source
3

Ce qui a fonctionné pour moi, c'est d'ajouter le fichier sous My Project/Java Resources/srcpuis d'utiliser

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

Je n'avais pas besoin d'ajouter explicitement ce fichier au chemin (l'ajouter à le /srcfait apparemment)

CommonCoreTawan
la source
Mauvaise syntaxe Java
Ilya Kharlamov
2

Je ne sais pas si cela est utile, mais dans mon cas, j'avais ma ressource dans le dossier / src / et j'obtenais cette erreur. J'ai ensuite déplacé l'image dans le dossier bin et cela a résolu le problème.

Leo Ufimtsev
la source
2

Assurez-vous que votre répertoire de ressources (par exemple "src") est dans votre chemin de classe (assurez-vous qu'il s'agit d'un répertoire source dans votre chemin de construction dans eclipse).

Assurez-vous que clazz est chargé depuis le chargeur de classe principal.

Ensuite, pour charger src / initialization / Lifepaths.txt, utilisez

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

Pourquoi: clazz.getResourcesAsStream(foo)recherche foo dans le classpath de clazz , par rapport au répertoire dans lequel clazz vit . Le premier "/" le charge à partir de la racine de n'importe quel répertoire dans le classpath de clazz.

À moins que vous ne soyez dans un conteneur d'un type quelconque, comme Tomcat, ou que vous fassiez quelque chose directement avec ClassLoaders, vous pouvez simplement traiter votre chemin de classe eclipse / ligne de commande comme le seul chemin de classe du chargeur de classe.

Bobby Martin
la source
2

Le chargeur de classe JVM par défaut utilisera le chargeur de classe parent pour charger les ressources en premier: deletegate-parent-classloader .

Lifepaths.class.getClass()Le chargeur de classe est bootstrap classloader, donc getResourceAsStreamrecherchera uniquement $ JAVA_HOME, quel que soit l'utilisateur fourni classpath. De toute évidence, Lifepaths.txt n'est pas là.

Lifepaths.classLe classloader de est system classpath classloader, de même getResourceAsStreamque la recherche définie par l'utilisateur classpathet Lifepaths.txt est là.

Lors de l'utilisation java.lang.Class#getResourceAsStream(String name), le nom qui ne commence pas par '/' sera ajouté avec package namecomme préfixe. Si vous voulez éviter cela, veuillez utiliser java.lang.ClassLoader#getResourceAsStream. Par exemple:

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

Grosso modo:

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

Supposons que la structure de votre projet soit la suivante:

├── 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
la source
2

si vous utilisez Maven, assurez-vous que votre emballage est «pot» et non «pom».

<packaging>jar</packaging>
Feku279
la source
0

Ce dont vous avez vraiment besoin est un chemin de classe absolu complet pour le fichier. Donc, au lieu de le deviner, essayez de trouver le ROOT, puis déplacez le fichier vers un meilleur emplacement base une structure de fichier <.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
la source
0

Ce qui a fonctionné pour moi, c'est que j'ai placé le fichier sous

src/main/java/myfile.log

et

InputStream is = getClass().getClassLoader().getResourceAsStream("myfile.log");
        
        if (is == null) {
            throw new FileNotFoundException("Log file not provided");
        }
Akash Yellappa
la source
Le dossier source est appelé src/main/JAVA, évidemment vos fichiers non codés ne doivent pas se trouver ici.
leyren
-12

@Emracool ... Je vous suggère une alternative. Puisque vous semblez essayer de charger un fichier * .txt. Mieux vaut utiliser FileInputStream()plutôt que ce getClass().getClassLoader().getResourceAsStream()ou ennuyeux getClass().getResourceAsStream(). Au moins, votre code s'exécutera correctement.

Jain
la source
quelle ??? -1 pour une telle réponse de travail. Peu importe ce que. Mais la solution suggérée ci-dessus fonctionnera à coup sûr.
Jain