J'ai un projet java
modules multiples sbt
et certains d'entre eux contiennent un dossier resources
.
InputStream templateIS = getClass().getResourceAsStream("/template.xls");
Après l'empaquetage j'ai obtenu:
URI template = getClass().getResource("/template.xls").toURI(); Files.newInputStream(Paths.get(template), StandardOpenOption.READ);
Et j'exécute mon programme comme n'importe quelle autre application java: java -cp "lib / *" MainClass
.
Mon problème est d'accéder au template.xls
depuis module-2.jar
.
Au début, j'ai essayé les lignes ci-dessous pour obtenir mon modèle:
lib/module-1.jar lib/module-2.jar
En mode développement , cela fonctionne. Mais pas sur le serveur (après le déploiement), il ne trouve pas la ressource.
java.nio.file.FileSystemNotFoundException: null [jar: fichier: /.../ lib / module-1.jar! /template.xls]
Après quelques recherches, j'ai modifié mon code d'accès comme suit pour qu'il fonctionne dans les deux modes (développement et déploiement):
module-1 resources template.xls module-2 resources other.xls
Je ne comprends pas pourquoi!
Quelle est la différence entre les deux méthodes?
3 Réponses :
Votre premier scénario ne fonctionne pas car le template.xls
est empaqueté dans un fichier jar. Ce n'est pas un fichier sur le système de fichiers (alors qu'il se trouve probablement dans votre environnement de développement avant l'empaquetage en tant que fichier). En tant que tel, l'API Files
ne peut pas le trouver.
Class.getResourceAsStream ()
utilise le mécanisme de chargement de classe, et la classe (évidemment) est chargée à partir du fichier .jar
.
mais pourquoi il le fonde avec getResourceAsStream
?
l'autre chose est que other.xls
est également dans le fichier jar mais je peux y accéder dans le premier scénario
@bubbles Parce que c'est à cela que sert getResourceAsStream ()
.
De la documentation , p>
La méthode getResource () renvoie une URL pour la ressource. L'URL (et sa représentation) est spécifique à l'implémentation et à la JVM (autrement dit, l'URL obtenue dans une instance d'exécution peut ne pas fonctionner dans un autre). Son protocole est généralement spécifique au chargement du ClassLoader la ressource. Si la ressource n'existe pas ou n'est pas visible en raison de considérations de sécurité, les méthodes renvoient null.
Si le code client veut lire le contenu de la ressource comme un InputStream, il peut appliquer la méthode openStream () sur l'URL. C'est assez commun pour justifier l'ajout de getResourceAsStream () à Class et ClassLoader. getResourceAsStream () identique à l'appel getResource (). openStream (), sauf que getResourceAsStream () intercepte Les exceptions IO renvoient un InputStream nul.
Donc, getResourceAsStream ()
équivaut à appeler getResource (). openStream ()
, sauf que getResourceAsStream ()
intercepte les retours d'exceptions IO un InputStream nul.
Files.newInputStream
, comme son nom l'indique, peut ouvrir des fichiers. Il ne peut rien ouvrir d'autre. C'est juste pour les fichiers.
Le concept de InputStream
est beaucoup plus abstrait. Lorsque vous ouvrez un fichier pour la lecture, vous obtenez un flux d'entrée, yup. Mais vous obtenez également des flux d'entrée pour bien d'autres choses: lecture à partir d'une connexion réseau; lire le contenu décompressé des fichiers zip. Lecture de la sortie d'une opération de décryptage. Le nom le dit vraiment: c'est une entrée, et c'est un flux de données. Ce qui s'applique à bien plus que «fichier sur un système de fichiers».
Paths.get (template)
produit un objet chemin, qui représente un fichier sur le système de fichiers. Si template
est dérivé d'un URI, cela ne fonctionne pas à moins que l'URI que vous avez ne soit un URI d'un objet fichier; la plupart des URI ne servent pas à classer les objets.
En mettant tout cela ensemble, dans votre premier échantillon, vous trouvez une ressource sur le chemin de classe (qui peut être des fichiers, mais pas nécessairement. Par exemple, il peut s'agir d'entrées dans un fichier jar), vous demandez ensuite son URI, transmettez-le à l'API Paths pour le transformer en objet Path, puis demandez à l'API Files de le transformer en InputStream, qui ne fonctionne que si l'URI représente un fichier.
Dans le deuxième extrait, vous demandez simplement au système de chargeur de classe de vous fournir un flux d'entrée. Il sait comment faire cela (après tout, java doit charger ces fichiers de classe!). Si la ressource que vous demandez se trouve être représentée par un fichier, elle va faire, en interne, plus ou moins la même chose que votre premier extrait de code: utilisez l'API Files pour ouvrir le fichier en lecture. Mais si c'est autre chose, il sait aussi comment faire cela - il sait également comment obtenir des ressources sur un réseau, à partir de fichiers jar internes, générés à la volée - le concept de chargement de classe (qui est ce que classe .getResource
vous permet d'accéder) est extrait.
NB: vous l'utilisez mal. La méthode appropriée est ClassYouAreWritingIn.class.getResource
et ClassYouAreWritingIn.class.getResourceAsStream
; getClass (). getResource
n'est pas correct; qui casse lors du sous-classement, contrairement à la forme correcte.
La différence est que les ressources ne sont pas des fichiers. Vous aviez déjà une URL dans le premier cas, à partir de laquelle vous auriez pu obtenir directement un
InputStream
, sans le détour invalide versFile
etFileInputStream
. Dans le second cas, vous êtes correctement allé directement auInputStream
sans le détour.