getResourceAsStream () vs FileInputStream

173

J'essayais de charger un fichier dans une application Web et j'obtenais une FileNotFoundexception lorsque j'utilisais FileInputStream. Cependant, en utilisant le même chemin, j'ai pu charger le fichier lorsque je l'ai fait getResourceAsStream(). Quelle est la différence entre les deux méthodes et pourquoi l'une fonctionne-t-elle tandis que l'autre ne fonctionne pas?

Vivin Paliath
la source

Réponses:

256

Les java.io.Fileconsorts et agissent sur le système de fichiers du disque local. La cause première de votre problème est que les chemins relatifs dans java.iodépendent du répertoire de travail actuel. C'est à dire le répertoire à partir duquel la JVM (dans votre cas: celle du serveur Web) est lancée. Cela peut par exemple être C:\Tomcat\binou quelque chose de complètement différent, mais donc pas C:\Tomcat\webapps\contextname ou ce que vous attendez de lui. Dans un projet Eclipse normal, ce serait C:\Eclipse\workspace\projectname. Vous pouvez en savoir plus sur le répertoire de travail actuel de la manière suivante:

System.out.println(new File(".").getAbsolutePath());

Cependant, le répertoire de travail n'est en aucun cas contrôlable par programme. Vous devriez vraiment préférer utiliser des chemins absolus dans l' FileAPI plutôt que des chemins relatifs. Par exempleC:\full\path\to\file.ext .

Vous ne voulez pas coder en dur ou deviner le chemin absolu dans les applications Java (Web). Ce n'est qu'un problème de portabilité (c'est-à-dire qu'il fonctionne dans le système X, mais pas dans le système Y). La pratique normale est de placer ce type de ressources dans le chemin de classe , ou d'ajouter son chemin complet au chemin de classe (dans un IDE comme Eclipse, c'est respectivement le srcdossier et le "chemin de construction"). De cette façon, vous pouvez les attraper à l'aide du ClassLoaderby ClassLoader#getResource()ou ClassLoader#getResourceAsStream(). Il est capable de localiser les fichiers par rapport à la "racine" du chemin de classe, comme vous l'avez compris par hasard. Dans les applications Web (ou toute autre application qui utilise plusieurs chargeurs de classe), il est recommandé d'utiliser le ClassLoadercomme renvoyé par Thread.currentThread().getContextClassLoader()pour cela afin que vous puissiez également regarder "en dehors" du contexte de l'application Web.

Une autre alternative dans les applications Web est le ServletContext#getResource()et son homologue ServletContext#getResourceAsStream(). Il peut accéder aux fichiers situés dans le webdossier public du projet webapp, y compris le /WEB-INFdossier. Le ServletContextest disponible dans les servlets par la getServletContext()méthode héritée , vous pouvez l'appeler tel quel.

Voir également:

BalusC
la source
27

getResourceAsStream est la bonne façon de le faire pour les applications Web (comme vous l'avez déjà appris).

La raison en est que la lecture à partir du système de fichiers ne peut pas fonctionner si vous empaquetez votre application Web dans un WAR. C'est la bonne façon de créer un package d'une application Web. C'est portable de cette façon, car vous n'êtes pas dépendant d'un chemin de fichier absolu ou de l'emplacement où votre serveur d'application est installé.

duffymo
la source
3
+1 - bien que "ne peut pas fonctionner" est trop fort. (La lecture à partir du système de fichiers peut fonctionner, mais le faire de manière portative est une tâche délicate ... et beaucoup plus de code, surtout si la ressource est dans un JAR.)
Stephen C
1
duffy, très belle réponse et vous avez expliqué quelle était mon erreur, mais BalusC est entré dans beaucoup de détails - je pense que sa réponse serait utile pour les personnes qui aimeraient également connaître les détails intérieurs. J'espère que cela ne vous dérange pas que je change la réponse acceptée par la sienne!
Vivin Paliath
@Stephen - Je ne pense pas que "ne peut pas travailler" soit trop fort. Même quelque chose d'aussi simple que d'être déployé sur deux serveurs différents avec des chemins différents vers le serveur d'applications le cassera. Le fait est que vous devez rendre votre WAR aussi autonome que possible. Votre argument est exact, mais je vais m'en tenir à mon libellé.
duffymo
14

FileInputStream chargera le chemin du fichier que vous passez au constructeur en tant que relatif à partir du répertoire de travail du processus Java. Habituellement, dans un conteneur Web, c'est quelque chose comme le bindossier.

getResourceAsStream()chargera un chemin de fichier relatif à partir du chemin de classe de votre application .

mat b
la source
12

La FileInputStreamclasse fonctionne directement avec le système de fichiers sous-jacent. Si le fichier en question n'y est pas physiquement présent, il ne pourra pas l'ouvrir. La getResourceAsStream()méthode fonctionne différemment. Il essaie de localiser et de charger la ressource à l'aide ClassLoaderde la classe sur laquelle elle est appelée. Cela lui permet de trouver, par exemple, des ressources incorporées dans des jarfichiers.

Poignard
la source
Eh bien, les fichiers dans un fichier jar sont toujours physiquement "présents" dans un système de fichiers, juste contenus dans d'autres fichiers
matt b
1
Eh bien, oui, bien sûr. Mais ils ne sont généralement pas considérés comme des entités indépendantes dans le système de fichiers, à moins que votre application ne connaisse le jarformat de fichier et ses implications. Et en Java, les appropriés ClassLoaderpourraient avoir cette connaissance, alors qu'une plaine FileInputStreamn'en a certainement pas.
Dirk
7

classname.getResourceAsStream () charge un fichier via le classloader de classname. Si la classe provient d'un fichier jar, c'est à partir de là que la ressource sera chargée.

FileInputStream est utilisé pour lire un fichier à partir du système de fichiers.

Lachlan Roche
la source
0

Je suis ici en séparant les deux utilisations en les marquant comme File Read (java.io) et Resource Read (ClassLoader.getResourceAsStream ()).

Lecture de fichier - 1. Fonctionne sur le système de fichiers local. 2. Tente de localiser le fichier demandé à partir du répertoire lancé par la JVM en tant que root 3. Idéalement bien lorsque vous utilisez des fichiers pour le traitement dans un emplacement prédéterminé comme, / dev / files ou C: \ Data.

Lecture de ressources - 1. Fonctionne sur le chemin d'accès aux classes 2. Tente de localiser le fichier / la ressource dans le chemin d'accès aux classes du chargeur de classe actuel ou parent. 3. Idéalement bon lorsque vous essayez de charger des fichiers à partir de fichiers packagés comme war ou jar.

Aditya Bhuyan
la source