237 votes

Différentes manières de charger un fichier en tant qu'InputStream

Quelle est la différence entre :

InputStream is = this.getClass().getClassLoader().getResourceAsStream(fileName)

y

InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)

y

InputStream is = this.getClass().getResourceAsStream(fileName)

Quand est-il plus approprié d'utiliser chacun d'eux que les autres ?

Le fichier que je veux lire se trouve dans le classpath de ma classe qui lit le fichier. Ma classe et le fichier sont dans le même jar et emballés dans un fichier EAR, et déployés dans WebSphere 6.1.

308voto

LordOfThePigs Points 3853

Il existe des différences subtiles quant à la manière dont les fileName que vous passez est interprété. En fait, vous avez deux méthodes différentes : ClassLoader.getResourceAsStream() y Class.getResourceAsStream() . Ces deux méthodes permettent de localiser la ressource de manière différente.

En Class.getResourceAsStream(path) le chemin est interprété comme un chemin local au paquetage de la classe à partir de laquelle vous l'appelez. Par exemple, appeler, String.class.getResourceAsStream("myfile.txt") cherchera un fichier dans votre classpath à l'emplacement suivant : "java/lang/myfile.txt" . Si votre chemin commence par un / alors il sera considéré comme un chemin absolu, et commencera la recherche à partir de la racine du classpath. Ainsi, appeler String.class.getResourceAsStream("/myfile.txt") cherchera l'emplacement suivant dans votre chemin de classe ./myfile.txt .

ClassLoader.getResourceAsStream(path) considérera que tous les chemins sont des chemins absolus. Ainsi, appeler String.class.getClassLoader().getResourceAsStream("myfile.txt") y String.class.getClassLoader().getResourceAsStream("/myfile.txt") chercheront tous deux un fichier dans votre classpath à l'emplacement suivant : ./myfile.txt .

Chaque fois que je mentionne un emplacement dans cet article, il peut s'agir d'un emplacement dans votre système de fichiers lui-même, ou dans le fichier jar correspondant, en fonction de la classe et/ou du ClassLoader à partir duquel vous chargez la ressource.

Dans votre cas, vous chargez la classe à partir d'un serveur d'application, vous devez donc utiliser la méthode suivante Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName) au lieu de this.getClass().getClassLoader().getResourceAsStream(fileName) . this.getClass().getResourceAsStream() fonctionnera également.

Lire cet article pour obtenir des informations plus détaillées sur ce problème particulier.


Avertissement pour les utilisateurs de Tomcat 7 et moins

L'une des réponses à cette question indique que mon explication semble être incorrecte pour Tomcat 7. J'ai essayé de chercher pourquoi ce serait le cas.

J'ai donc regardé le code source de Tomcat. WebAppClassLoader pour plusieurs versions de Tomcat. L'implémentation de findResource(String name) (qui est utilement responsable de la production de l'URL de la ressource demandée) est pratiquement identique dans Tomcat 6 et Tomcat 7, mais est différent dans Tomcat 8.

Dans les versions 6 et 7, l'implémentation ne tente pas de normaliser le nom de la ressource. Cela signifie que dans ces versions, classLoader.getResourceAsStream("/resource.txt") peut ne pas produire le même résultat que classLoader.getResourceAsStream("resource.txt") alors qu'il le devrait (puisque c'est ce que spécifie la Javadoc). [code source]

Dans la version 8 cependant, le nom de la ressource est normalisé pour garantir que la version absolue du nom de la ressource est celle qui est utilisée. Par conséquent, dans Tomcat 8, les deux appels décrits ci-dessus devraient toujours retourner le même résultat. [code source]

Par conséquent, vous devez être très prudent lorsque vous utilisez ClassLoader.getResourceAsStream() o Class.getResourceAsStream() sur les versions de Tomcat antérieures à 8. Et vous devez également garder à l'esprit que class.getResourceAsStream("/resource.txt") appelle effectivement classLoader.getResourceAsStream("resource.txt") (le chef de file / est supprimée).

2 votes

Je suis presque sûr que getClass().getResourceAsStream("/myfile.txt") se comporte différemment de getClassLoader().getResourceAsStream("/myfile.txt") .

0 votes

@BrianGordon : Ils ne se comportent pas différemment. En fait, la javadoc pour Class.getResourceAsStream(String) dit la chose suivante : "Cette méthode délègue au chargeur de classe de cet objet", et donne ensuite un tas de règles sur la façon dont elle convertit un chemin relatif en chemin absolu avant de déléguer au chargeur de classe.

0 votes

@LordOfThePigs Regardez la source réelle. Class.getResourceAsStream supprime le slash avant si vous fournissez un chemin absolu.

20voto

Utilice MyClass.class.getClassLoader().getResourceAsStream(path) pour charger les ressources associées à votre code. Utilisez MyClass.class.getResourceAsStream(path) comme raccourci, et pour les ressources empaquetées dans le paquetage de votre classe.

Utilice Thread.currentThread().getContextClassLoader().getResourceAsStream(path) pour obtenir des ressources qui font partie du code client et qui ne sont pas étroitement liées au code appelant. Vous devez être prudent avec cela car le chargeur de classe du contexte du fil pourrait pointer vers n'importe quoi.

6voto

John Lonergan Points 109

Le simple vieux Java sur le simple vieux Java 7 et sans autres dépendances montre la différence...

J'ai mis file.txt en c:\temp\ et j'ai mis c:\temp\ sur le classpath.

Il n'y a qu'un seul cas où il y a une différence entre les deux appels.

class J {

 public static void main(String[] a) {
    // as "absolute"

    // ok   
    System.err.println(J.class.getResourceAsStream("/file.txt") != null); 

    // pop            
    System.err.println(J.class.getClassLoader().getResourceAsStream("/file.txt") != null); 

    // as relative

    // ok
    System.err.println(J.class.getResourceAsStream("./file.txt") != null); 

    // ok
    System.err.println(J.class.getClassLoader().getResourceAsStream("./file.txt") != null); 

    // no path

    // ok
    System.err.println(J.class.getResourceAsStream("file.txt") != null); 

   // ok
   System.err.println(J.class.getClassLoader().getResourceAsStream("file.txt") != null); 
  }
}

0 votes

Merci beaucoup, mais pour moi, seul 'J.class.getResourceAsStream("file.txt")' a fonctionné.

4voto

Tim Büthe Points 21527

Toutes ces réponses par ici, ainsi que les réponses en cette question suggèrent que le chargement d'URL absolus, comme "/foo/bar.properties", est traité de la même manière par la Commission européenne. class.getResourceAsStream(String) y class.getClassLoader().getResourceAsStream(String) . Ce n'est PAS le cas, du moins pas dans ma configuration/version de Tomcat (actuellement 7.0.40).

MyClass.class.getResourceAsStream("/foo/bar.properties"); // works!  
MyClass.class.getClassLoader().getResourceAsStream("/foo/bar.properties"); // does NOT work!

Désolé, je n'ai absolument aucune explication satisfaisante, mais je suppose que tomcat fait de sales tours et sa magie noire avec les classloaders et cause la différence. J'ai toujours utilisé class.getResourceAsStream(String) dans le passé et n'ont pas eu de problèmes.

PS : J'ai aussi posté ceci sur aquí

0 votes

Peut-être que tomcat décide de ne pas respecter la spécification et, et ne traite pas tous les chemins passés ClassLoader.getResourceAsStream() comme absolu ? C'est plausible car comme mentionné dans certains commentaires ci-dessus, Class.getResourceAsStream appelle en fait getClassLoader().getResourceAsStream` mais supprime tout slash en tête.

0 votes

Après avoir vérifié dans le code source de Java SE, je pense que je tiens la réponse : Les deux Class.getResourceAsStream() y ClassLoader.getResourceAsStream() finissent par appeler ClassLoader.findResource() qui est une méthode protégée dont l'implémentation par défaut est vide, mais dont la javadoc indique explicitement "Les implémentations de chargeurs de classes doivent surcharger cette méthode pour spécifier où trouver les ressources". Je soupçonne que l'implémentation de tomcat de cette méthode particulière peut être défectueuse.

0 votes

J'ai également comparé l'implémentation de WebAppClassLoader.findResource(String name) en Tomcat 7 avec celle de Tomcat 8 et il semble qu'il y ait une différence essentielle. Tomcat 8 normalise explicitement le nom de la ressource en ajoutant un préfixe / s'il n'en contient pas, ce qui rend tous les noms absolus. Tomcat 7 ne le fait pas. C'est clairement un bug de Tomcat 7.

3voto

Toni Almeida Points 3406

Après avoir essayé plusieurs façons de charger le fichier sans succès, je me suis souvenu que je pouvais utiliser FileInputStream ce qui a parfaitement fonctionné.

InputStream is = new FileInputStream("file.txt");

Il s'agit d'une autre manière de lire un fichier dans un fichier InputStream il lit le fichier dans le dossier en cours d'exécution.

0 votes

Ce n'est pas un fichier, c'est une ressource. La réponse n'est pas correcte.

1 votes

@EJP Je me retrouve dans cette réponse SO, à chercher des moyens de charger un fichier, sans savoir la différence entre un fichier et une ressource. Je ne vais pas supprimer ma réponse car elle peut aider d'autres personnes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X