Comment lire un fichier de ressources à partir d’un fichier jar Java?

J’essaie d’accéder à un fichier XML dans un fichier jar, à partir d’un fichier jar distinct qui s’exécute en tant qu’application de bureau. Je peux obtenir l’URL du fichier dont j’ai besoin, mais lorsque je le transmets à un FileReader (en tant que chaîne), je reçois une exception FileNotFoundException indiquant “Le nom du fichier, le nom du répertoire ou la syntaxe de l’étiquette de volume sont incorrects”.

À titre de référence, je n’ai aucun problème à lire les ressources d’image du même fichier jar, en passant l’URL à un constructeur ImageIcon. Cela semble indiquer que la méthode que j’utilise pour obtenir l’URL est correcte.

URL url = getClass().getResource("/xxx/xxx/xxx/services.xml"); ServicesLoader jsl = new ServicesLoader( url.toSsortingng() ); 

Dans la classe ServicesLoader j’ai

 XMLReader xr = XMLReaderFactory.createXMLReader(); xr.setContentHandler( this ); xr.setErrorHandler( this ); xr.parse( new InputSource( new FileReader( filename ))); 

Quel est le problème avec l’utilisation de cette technique pour lire le fichier XML?

On dirait que vous voulez utiliser java.lang.Class.getResourceAsStream(Ssortingng) , voir

http://java.sun.com/javase/6/docs/api/java/lang/Class.html#getResourceAsStream(java.lang.Ssortingng)

Vous ne dites pas s’il s’agit d’une application de bureau ou Web. J’utiliserais la méthode getResourceAsStream() d’un ClassLoader approprié si c’est un bureau ou le contexte s’il s’agit d’une application Web.

Il semble que vous utilisiez le résultat URL.toSsortingng comme argument du constructeur FileReader . URL.toSsortingng est un peu cassé, et vous devriez plutôt utiliser url.toURI().toSsortingng() . Dans tous les cas, la chaîne n’est pas un chemin de fichier.

Au lieu de cela, vous devriez soit:

  • Transmettez l’ URL à ServicesLoader et laissez-le appeler openStream ou similaire.
  • Utilisez Class.getResourceAsStream et transmettez simplement le stream, éventuellement à l’intérieur d’un InputSource . (N’oubliez pas de vérifier les valeurs nulles car l’API est un peu compliquée.)

Le problème était que j’allais trop loin en appelant la méthode d’parsing de XMLReader. La méthode d’parsing accepte un InputSource, il n’y a donc aucune raison d’utiliser un FileReader. Changer la dernière ligne du code ci-dessus pour

 xr.parse( new InputSource( filename )); 

fonctionne très bien.

Je tiens à souligner que l’un des problèmes est de savoir si les mêmes ressources sont dans plusieurs fichiers JAR. Disons que vous voulez lire /org/node/foo.txt, mais pas à partir d’un fichier, mais à partir de chaque fichier jar.

J’ai déjà rencontré ce même problème plusieurs fois auparavant. J’espérais dans JDK 7 que quelqu’un écrirait un système de fichiers classpath, mais hélas pas encore.

Spring a la classe Resource qui vous permet de charger les ressources classpath assez bien.

J’ai écrit un petit prototype pour résoudre ce problème très important de lecture des ressources de plusieurs fichiers jar. Le prototype ne gère pas tous les cas extrêmes, mais il recherche les ressources dans les répertoires des fichiers jar.

J’ai utilisé Stack Overflow pendant un certain temps. C’est la deuxième réponse dont je me souviens avoir répondu à une question, alors pardonnez-moi si je vais trop longtemps (c’est ma nature).

Ceci est un lecteur de ressource prototype. Le prototype est dépourvu de vérification d’erreur robuste.

J’ai deux fichiers JAR prototypes que j’ai configurés.

  <pre> <dependency> <groupId>invoke</groupId> <artifactId>invoke</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>node</groupId> <artifactId>node</artifactId> <version>1.0-SNAPSHOT</version> </dependency> 

Les fichiers jar ont chacun un fichier sous / org / node / appelé resource.txt.

Ceci est juste un prototype de ce à quoi un gestionnaire ressemblerait avec classpath: // J’ai aussi un fichier resource.foo.txt dans mes ressources locales pour ce projet.

Il les ramasse tous et les imprime.

   package com.foo; import java.io.File; import java.io.FileReader; import java.io.InputStreamReader; import java.io.Reader; import java.net.URI; import java.net.URL; import java.util.Enumeration; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; /** * Prototype resource reader. * This prototype is devoid of error checking. * * * I have two prototype jar files that I have setup. * 
 *  * invoke * invoke * 1.0-SNAPSHOT *  * *  * node * node * 1.0-SNAPSHOT *  * 

* The jar files each have a file under /org/node/ called resource.txt. *
* This is just a prototype of what a handler would look like with classpath:// * I also have a resource.foo.txt in my local resources for this project. *
*/ public class ClasspathReader { public static void main(Ssortingng[] args) throws Exception { /* This project includes two jar files that each have a resource located in /org/node/ called resource.txt. */ /* Name space is just a device I am using to see if a file in a dir starts with a name space. Think of namespace like a file extension but it is the start of the file not the end. */ Ssortingng namespace = "resource"; //someResource is classpath. Ssortingng someResource = args.length > 0 ? args[0] : //"classpath:///org/node/resource.txt"; It works with files "classpath:///org/node/"; //It also works with directories URI someResourceURI = URI.create(someResource); System.out.println("URI of resource = " + someResourceURI); someResource = someResourceURI.getPath(); System.out.println("PATH of resource =" + someResource); boolean isDir = !someResource.endsWith(".txt"); /** Classpath resource can never really start with a starting slash. * Logically they do, but in reality you have to ssortingp it. * This is a known behavior of classpath resources. * It works with a slash unless the resource is in a jar file. * Bottom line, by ssortingpping it, it always works. */ if (someResource.startsWith("/")) { someResource = someResource.subssortingng(1); } /* Use the ClassLoader to lookup all resources that have this name. Look for all resources that match the location we are looking for. */ Enumeration resources = null; /* Check the context classloader first. Always use this if available. */ try { resources = Thread.currentThread().getContextClassLoader().getResources(someResource); } catch (Exception ex) { ex.printStackTrace(); } if (resources == null || !resources.hasMoreElements()) { resources = ClasspathReader.class.getClassLoader().getResources(someResource); } //Now iterate over the URLs of the resources from the classpath while (resources.hasMoreElements()) { URL resource = resources.nextElement(); /* if the resource is a file, it just means that we can use normal mechanism to scan the directory. */ if (resource.getProtocol().equals("file")) { //if it is a file then we can handle it the normal way. handleFile(resource, namespace); continue; } System.out.println("Resource " + resource); /* Split up the ssortingng that looks like this: jar:file:/Users/rick/.m2/repository/invoke/invoke/1.0-SNAPSHOT/invoke-1.0-SNAPSHOT.jar!/org/node/ into this /Users/rick/.m2/repository/invoke/invoke/1.0-SNAPSHOT/invoke-1.0-SNAPSHOT.jar and this /org/node/ */ Ssortingng[] split = resource.toSsortingng().split(":"); Ssortingng[] split2 = split[2].split("!"); Ssortingng zipFileName = split2[0]; Ssortingng sresource = split2[1]; System.out.printf("After split zip file name = %s," + " \nresource in zip %s \n", zipFileName, sresource); /* Open up the zip file. */ ZipFile zipFile = new ZipFile(zipFileName); /* Iterate through the ensortinges. */ Enumeration ensortinges = zipFile.ensortinges(); while (ensortinges.hasMoreElements()) { ZipEntry entry = ensortinges.nextElement(); /* If it is a directory, then skip it. */ if (entry.isDirectory()) { continue; } Ssortingng entryName = entry.getName(); System.out.printf("zip entry name %s \n", entryName); /* If it does not start with our someResource Ssortingng then it is not our resource so continue. */ if (!entryName.startsWith(someResource)) { continue; } /* the fileName part from the entry name. * where /foo/bar/foo/bee/bar.txt, bar.txt is the file */ Ssortingng fileName = entryName.subssortingng(entryName.lastIndexOf("/") + 1); System.out.printf("fileName %s \n", fileName); /* See if the file starts with our namespace and ends with our extension. */ if (fileName.startsWith(namespace) && fileName.endsWith(".txt")) { /* If you found the file, print out the contents fo the file to System.out.*/ try (Reader reader = new InputStreamReader(zipFile.getInputStream(entry))) { SsortingngBuilder builder = new SsortingngBuilder(); int ch = 0; while ((ch = reader.read()) != -1) { builder.append((char) ch); } System.out.printf("zip fileName = %s\n\n####\n contents of file %s\n###\n", entryName, builder); } catch (Exception ex) { ex.printStackTrace(); } } //use the entry to see if it's the file '1.txt' //Read from the byte using file.getInputStream(entry) } } } /** * The file was on the file system not a zip file, * this is here for completeness for this example. * otherwise. * * @param resource * @param namespace * @throws Exception */ private static void handleFile(URL resource, Ssortingng namespace) throws Exception { System.out.println("Handle this resource as a file " + resource); URI uri = resource.toURI(); File file = new File(uri.getPath()); if (file.isDirectory()) { for (File childFile : file.listFiles()) { if (childFile.isDirectory()) { continue; } Ssortingng fileName = childFile.getName(); if (fileName.startsWith(namespace) && fileName.endsWith("txt")) { try (FileReader reader = new FileReader(childFile)) { SsortingngBuilder builder = new SsortingngBuilder(); int ch = 0; while ((ch = reader.read()) != -1) { builder.append((char) ch); } System.out.printf("fileName = %s\n\n####\n contents of file %s\n###\n", childFile, builder); } catch (Exception ex) { ex.printStackTrace(); } } } } else { Ssortingng fileName = file.getName(); if (fileName.startsWith(namespace) && fileName.endsWith("txt")) { try (FileReader reader = new FileReader(file)) { SsortingngBuilder builder = new SsortingngBuilder(); int ch = 0; while ((ch = reader.read()) != -1) { builder.append((char) ch); } System.out.printf("fileName = %s\n\n####\n contents of file %s\n###\n", fileName, builder); } catch (Exception ex) { ex.printStackTrace(); } } } } }

package com.foo; import java.io.File; import java.io.FileReader; import java.io.InputStreamReader; import java.io.Reader; import java.net.URI; import java.net.URL; import java.util.Enumeration; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; /** * Prototype resource reader. * This prototype is devoid of error checking. * * * I have two prototype jar files that I have setup. *

 *  * invoke * invoke * 1.0-SNAPSHOT *  * *  * node * node * 1.0-SNAPSHOT *  * 

* The jar files each have a file under /org/node/ called resource.txt. *
* This is just a prototype of what a handler would look like with classpath:// * I also have a resource.foo.txt in my local resources for this project. *
*/ public class ClasspathReader { public static void main(Ssortingng[] args) throws Exception { /* This project includes two jar files that each have a resource located in /org/node/ called resource.txt. */ /* Name space is just a device I am using to see if a file in a dir starts with a name space. Think of namespace like a file extension but it is the start of the file not the end. */ Ssortingng namespace = "resource"; //someResource is classpath. Ssortingng someResource = args.length > 0 ? args[0] : //"classpath:///org/node/resource.txt"; It works with files "classpath:///org/node/"; //It also works with directories URI someResourceURI = URI.create(someResource); System.out.println("URI of resource = " + someResourceURI); someResource = someResourceURI.getPath(); System.out.println("PATH of resource =" + someResource); boolean isDir = !someResource.endsWith(".txt"); /** Classpath resource can never really start with a starting slash. * Logically they do, but in reality you have to ssortingp it. * This is a known behavior of classpath resources. * It works with a slash unless the resource is in a jar file. * Bottom line, by ssortingpping it, it always works. */ if (someResource.startsWith("/")) { someResource = someResource.subssortingng(1); } /* Use the ClassLoader to lookup all resources that have this name. Look for all resources that match the location we are looking for. */ Enumeration resources = null; /* Check the context classloader first. Always use this if available. */ try { resources = Thread.currentThread().getContextClassLoader().getResources(someResource); } catch (Exception ex) { ex.printStackTrace(); } if (resources == null || !resources.hasMoreElements()) { resources = ClasspathReader.class.getClassLoader().getResources(someResource); } //Now iterate over the URLs of the resources from the classpath while (resources.hasMoreElements()) { URL resource = resources.nextElement(); /* if the resource is a file, it just means that we can use normal mechanism to scan the directory. */ if (resource.getProtocol().equals("file")) { //if it is a file then we can handle it the normal way. handleFile(resource, namespace); continue; } System.out.println("Resource " + resource); /* Split up the ssortingng that looks like this: jar:file:/Users/rick/.m2/repository/invoke/invoke/1.0-SNAPSHOT/invoke-1.0-SNAPSHOT.jar!/org/node/ into this /Users/rick/.m2/repository/invoke/invoke/1.0-SNAPSHOT/invoke-1.0-SNAPSHOT.jar and this /org/node/ */ Ssortingng[] split = resource.toSsortingng().split(":"); Ssortingng[] split2 = split[2].split("!"); Ssortingng zipFileName = split2[0]; Ssortingng sresource = split2[1]; System.out.printf("After split zip file name = %s," + " \nresource in zip %s \n", zipFileName, sresource); /* Open up the zip file. */ ZipFile zipFile = new ZipFile(zipFileName); /* Iterate through the ensortinges. */ Enumeration ensortinges = zipFile.ensortinges(); while (ensortinges.hasMoreElements()) { ZipEntry entry = ensortinges.nextElement(); /* If it is a directory, then skip it. */ if (entry.isDirectory()) { continue; } Ssortingng entryName = entry.getName(); System.out.printf("zip entry name %s \n", entryName); /* If it does not start with our someResource Ssortingng then it is not our resource so continue. */ if (!entryName.startsWith(someResource)) { continue; } /* the fileName part from the entry name. * where /foo/bar/foo/bee/bar.txt, bar.txt is the file */ Ssortingng fileName = entryName.subssortingng(entryName.lastIndexOf("/") + 1); System.out.printf("fileName %s \n", fileName); /* See if the file starts with our namespace and ends with our extension. */ if (fileName.startsWith(namespace) && fileName.endsWith(".txt")) { /* If you found the file, print out the contents fo the file to System.out.*/ try (Reader reader = new InputStreamReader(zipFile.getInputStream(entry))) { SsortingngBuilder builder = new SsortingngBuilder(); int ch = 0; while ((ch = reader.read()) != -1) { builder.append((char) ch); } System.out.printf("zip fileName = %s\n\n####\n contents of file %s\n###\n", entryName, builder); } catch (Exception ex) { ex.printStackTrace(); } } //use the entry to see if it's the file '1.txt' //Read from the byte using file.getInputStream(entry) } } } /** * The file was on the file system not a zip file, * this is here for completeness for this example. * otherwise. * * @param resource * @param namespace * @throws Exception */ private static void handleFile(URL resource, Ssortingng namespace) throws Exception { System.out.println("Handle this resource as a file " + resource); URI uri = resource.toURI(); File file = new File(uri.getPath()); if (file.isDirectory()) { for (File childFile : file.listFiles()) { if (childFile.isDirectory()) { continue; } Ssortingng fileName = childFile.getName(); if (fileName.startsWith(namespace) && fileName.endsWith("txt")) { try (FileReader reader = new FileReader(childFile)) { SsortingngBuilder builder = new SsortingngBuilder(); int ch = 0; while ((ch = reader.read()) != -1) { builder.append((char) ch); } System.out.printf("fileName = %s\n\n####\n contents of file %s\n###\n", childFile, builder); } catch (Exception ex) { ex.printStackTrace(); } } } } else { Ssortingng fileName = file.getName(); if (fileName.startsWith(namespace) && fileName.endsWith("txt")) { try (FileReader reader = new FileReader(file)) { SsortingngBuilder builder = new SsortingngBuilder(); int ch = 0; while ((ch = reader.read()) != -1) { builder.append((char) ch); } System.out.printf("fileName = %s\n\n####\n contents of file %s\n###\n", fileName, builder); } catch (Exception ex) { ex.printStackTrace(); } } } } }

Vous pouvez voir un exemple plus complet avec l’exemple de sortie.

En dehors de votre technique, pourquoi ne pas utiliser la classe Java JarFile standard pour obtenir les références souhaitées? À partir de là, la plupart de vos problèmes devraient disparaître.

Si vous utilisez intensivement les ressources, vous pouvez envisager d’utiliser Commons VFS .

Prend également en charge: * Fichiers locaux * FTP, SFTP * HTTP et HTTPS * Fichiers temporaires “FS normal sauvegardé” * Zip, Jar et Tar (non compressé, tgz ou tbz2) * ressources gzip et bzip2 * * ram – “ramdrive” * mime

Il y a aussi JBoss VFS – mais ce n’est pas très documenté.

J’ai 2 fichiers CSV que j’utilise pour lire des données. Le programme Java est exporté en tant que fichier jar exécutable. Lorsque vous l’exportez, vous découvrirez qu’il n’exporte pas vos ressources.

J’ai ajouté un dossier sous projet appelé données en éclipse. Dans ce dossier, j’ai stocké mes fichiers csv.

Quand j’ai besoin de référencer ces fichiers, je le fais comme ça …

 private static final Ssortingng ZIP_FILE_LOCATION_PRIMARY = "free-zipcode-database-Primary.csv"; private static final Ssortingng ZIP_FILE_LOCATION = "free-zipcode-database.csv"; private static Ssortingng getFileLocation(){ Ssortingng loc = new File("").getAbsolutePath() + File.separatorChar + "data" + File.separatorChar; if (usePrimaryZipCodesOnly()){ loc = loc.concat(ZIP_FILE_LOCATION_PRIMARY); } else { loc = loc.concat(ZIP_FILE_LOCATION); } return loc; } 

Ensuite, lorsque vous placez le fichier jar dans un emplacement afin qu’il puisse être exécuté via la ligne de commande, assurez-vous d’append le dossier de données contenant les ressources au même emplacement que le fichier jar.