64 votes

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

J'essaye d'accéder à un fichier XML dans un fichier jar, à partir d'un autre pot qui s'exécute comme une application de bureau. Je peux obtenir l'URL du fichier que j'ai besoin, mais quand je passe que pour un FileReader (comme une Chaîne de caractères), je reçois un FileNotFoundException en disant: "Le nom de fichier, le nom du répertoire ou de la syntaxe du nom de volume est incorrecte."

Comme point de référence, je n'ai aucun problème de lecture de l'image des ressources à partir de la même bocal, en passant l'URL d'une ImageIcon constructeur. 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.toString() );

À l'intérieur de la ServicesLoader classe I ont

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?

71voto

iny Points 3925

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

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

5voto

duffymo Points 188155

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é s'il s'agit d'un ordinateur de bureau ou du contexte s'il s'agit d'une application Web.

4voto

Il semble que si vous utilisez l' URL.toString résultat que l'argument de l' FileReader constructeur. URL.toString est un peu cassé, et au lieu de cela, vous devez généralement utiliser url.toURI().toString(). En tout cas, la chaîne n'est pas un chemin d'accès au fichier.

Au lieu de cela, vous devez:

  • Passer l' URL de ServicesLoader et le laisser appeler openStream ou similaire.
  • Utiliser Class.getResourceAsStream et vient de passer le flux, éventuellement à l'intérieur d'un InputSource. (N'oubliez pas de vérifier les valeurs null comme l'API est un peu brouillon.)

4voto

Bill the Lizard Points 147311

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

 xr.parse( new InputSource( filename ));
 

fonctionne très bien.

4voto

RickHigh Points 424

Je tiens à souligner que l'un des problèmes est que 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 rencontré ce même problème plusieurs fois avant. J'espérais dans le JDK 7 que quelqu'un serait d'écrire un classpath du système de fichiers, mais hélas pas encore.

Le printemps est la classe de Ressource qui vous permet de charger le classpath de ressources tout à fait bien.

J'ai écrit un petit prototype de résoudre ce problème de ressources de lecture de plusieurs fichiers jar. Le prototype ne gère pas tous les cas de bord, mais il ne poignée de chercher des ressources dans les répertoires qui sont dans les fichiers jar.

J'ai utilisé un Débordement de Pile très peu de temps. C'est la deuxième réponse que je me souviens de répondre à une question, donc pardonnez-moi si je vais trop long (c'est ma nature).

C'est un prototype de ressources reader. Le prototype est dépourvue de robuste de vérification d'erreur.

J'ai deux prototypes de fichiers jar que j'ai de l'installation.

 <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/ a appelé resource.txt.

C'est juste un prototype de ce qu'est un gestionnaire pourrait ressembler à classpath:// J'ai aussi un resource.foo.txt dans mon local de ressources pour ce projet.

Il les ramasse et les imprime sur.

   

    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.
    * <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>
    * </pre>
    * The jar files each have a file under /org/node/ called resource.txt.
    * <br />
    * 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.
    * <br />
    */
    public class ClasspathReader {

        public static void main(String[] 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.
            */
            String namespace = "resource";

            //someResource is classpath.
            String 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 strip 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 stripping it, it always works.
             */
            if (someResource.startsWith("/")) {
                someResource = someResource.substring(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 string 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/
                */
                String[] split = resource.toString().split(":");
                String[] split2 = split[2].split("!");
                String zipFileName = split2[0];
                String 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 entries.  */
                Enumeration entries = zipFile.entries();

                while (entries.hasMoreElements()) {
                    ZipEntry entry = entries.nextElement();
                    /* If it is a directory, then skip it. */
                    if (entry.isDirectory()) {
                        continue;
                    }

                    String entryName = entry.getName();
                    System.out.printf("zip entry name %s \n", entryName);

                    /* If it does not start with our someResource String
                       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
                     */
                    String fileName = entryName.substring(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))) {
                            StringBuilder builder = new StringBuilder();
                            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, String 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;
                    }
                    String fileName = childFile.getName();
                    if (fileName.startsWith(namespace) && fileName.endsWith("txt")) {

                        try (FileReader reader = new FileReader(childFile)) {
                            StringBuilder builder = new StringBuilder();
                            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 {
                String fileName = file.getName();
                if (fileName.startsWith(namespace) && fileName.endsWith("txt")) {

                    try (FileReader reader = new FileReader(file)) {
                        StringBuilder builder = new StringBuilder();
                        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 fuller exemple ici avec l'exemple de sortie.

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