web-dev-qa-db-fra.com

Différentes façons de charger un fichier en tant que InputStream

Quelle est la différence entre:

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

et

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

et

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

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

Le fichier que je veux lire se trouve dans le chemin de classe en tant que classe qui lit le fichier. Ma classe et le fichier se trouvent dans le même fichier jar et sont packagés dans un fichier EAR et déployés dans WebSphere 6.1.

207
zqudlyba

Il existe des différences subtiles quant à la façon dont la fileName que vous passez est interprétée. En gros, vous avez 2 méthodes différentes: ClassLoader.getResourceAsStream() et Class.getResourceAsStream(). Ces deux méthodes localiseront la ressource différemment.

Dans Class.getResourceAsStream(path), le chemin est interprété comme un chemin local au paquet de la classe à partir de laquelle vous l'appelez. Par exemple, appeler String.getResourceAsStream("myfile.txt") recherchera un fichier dans votre chemin de classe à l'emplacement suivant: "Java/lang/myfile.txt". Si votre chemin commence par un /, il sera considéré comme un chemin absolu et démarrera la recherche à partir de la racine du chemin de classe. Donc, l'appel de String.getResourceAsStream("/myfile.txt") examinera l'emplacement suivant dans votre chemin de classe ./myfile.txt.

ClassLoader.getResourceAsStream(path) considérera tous les chemins comme des chemins absolus. Ainsi, appeler String.getClassLoader().getResourceAsStream("myfile.txt") et String.getClassLoader().getResourceAsStream("/myfile.txt") rechercheront tous deux un fichier dans votre chemin de classe à l'emplacement suivant: ./myfile.txt.

Chaque fois que je mentionne un emplacement dans ce message, il peut s'agir d'un emplacement dans votre système de fichiers lui-même ou à l'intérieur du fichier jar correspondant, en fonction de la classe et/ou du chargeur de classe à partir duquel vous chargez la ressource.

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

Lisez cet article pour plus d'informations sur ce problème particulier.


Avertissement pour les utilisateurs de Tomcat 7 et inférieur

L'une des réponses à cette question indique que mon explication semble être incorrecte pour Tomcat 7. J'ai essayé de regarder autour de moi pour comprendre pourquoi.

J'ai donc examiné le code source de WebAppClassLoader de Tomcat pour plusieurs versions de Tomcat. L'implémentation de findResource(String name) (qui est essentiellement responsable de la production de l'URL de la ressource demandée) est pratiquement identique dans Tomcat 6 et Tomcat 7, mais est différente 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 l'événement classLoader.getResourceAsStream("resource.txt") comme il le devrait (car c'est ce que spécifie le Javadoc). [code source]

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

Par conséquent, vous devez faire très attention lorsque vous utilisez ClassLoader.getResourceAsStream() ou Class.getResourceAsStream() sur les versions de Tomcat antérieures à 8. Et vous devez également vous rappeler que class.getResourceAsStream("/resource.txt") appelle en fait classLoader.getResourceAsStream("resource.txt") (le / principal est supprimé).

284
LordOfThePigs

Utilisez MyClass.class.getClassLoader().getResourceAsStream(path) pour charger la ressource associée à votre code. Utilisez MyClass.class.getResourceAsStream(path) comme raccourci et pour les ressources contenues dans le package de votre classe.

Utilisez Thread.currentThread().getContextClassLoader().getResourceAsStream(path) pour obtenir des ressources qui font partie du code client et ne sont pas étroitement liées au code appelant. Soyez prudent avec ceci car le chargeur de classe de contexte de thread pourrait pointer n'importe quoi.

20

Plain old Java sur plain plain Java 7 et aucune autre dépendance ne montre la différence ...

J'ai mis file.txt dans c:\temp\ et j'ai mis c:\temp\ sur le chemin de classe.

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); 
  }
}
6
John Lonergan

Toutes ces réponses ici, ainsi que les réponses dans cette question , suggèrent que le chargement d’URL absolues, comme "/foo/bar.properties", a été traité de la même manière par class.getResourceAsStream(String) et class.getClassLoader().getResourceAsStream(String). Ce n'est PAS le cas, du moins pas dans ma configuration/version 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 des tours sales et sa magie noire avec les chargeurs de classe et fait la différence. J'ai toujours utilisé class.getResourceAsStream(String) par le passé et je n'ai rencontré aucun problème.

PS: J'ai aussi posté ceci sur ici

3
Tim Büthe

Après avoir essayé de charger le fichier sans succès, je me suis rappelé que je pouvais utiliser FileInputStream, qui fonctionnait parfaitement.

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

C'est une autre façon de lire un fichier dans un InputStream, il lit le fichier à partir du dossier en cours d'exécution.

0
António Almeida