Question

J'essaie d'accéder à un fichier XML dans un fichier jar, à partir d'un fichier jar séparé 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), une exception FileNotFoundException indiquant "Le nom du fichier, le nom du répertoire ou la syntaxe du libellé de volume est incorrect".

En tant que référence, je n’ai aucune difficulté à lire les ressources d’image du même fichier jar, à transmettre 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.toString() );

J'ai la classe ServicesLoader

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

Quel est le problème avec cette technique pour lire le fichier XML?

Était-ce utile?

La solution 4

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.

Autres conseils

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)

Vous ne dites pas s'il s'agit d'un bureau ou d'une application Web. J'utiliserais la méthode getResourceAsStream () à partir d'un ClassLoader approprié s'il s'agit d'un bureau ou du contexte s'il s'agit d'une application Web.

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

Au lieu de cela, vous devez soit:

  • Transmettez le URL à ServiceLoader et laissez-le appeler openStream ou similaire.
  • Utilisez Class.getResourceAsStream et transmettez simplement le flux, éventuellement à l'intérieur d'un InputSource . (N'oubliez pas de vérifier les valeurs NULL car l'API est un peu en désordre.)

Je tiens à souligner qu’un problème est que si les mêmes ressources sont dans plusieurs fichiers JAR. Supposons que vous souhaitiez lire /org/node/foo.txt, mais pas à partir d'un fichier, mais de chaque fichier jar.

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

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

J'ai écrit un petit prototype pour résoudre ce problème de lecture des ressources à partir de plusieurs fichiers jar. Le prototype ne gère pas tous les cas, mais la recherche de ressources dans les répertoires contenus dans les fichiers jar.

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

Ceci est un prototype de lecteur de ressources. Le prototype est dépourvu de vérification d'erreur robuste.

J'ai configuré deux fichiers jar prototypes.

 <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 également un resource.foo.txt dans mes ressources locales pour ce projet.

Il les prend 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.
    * <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 exemple plus complet ici avec l'exemple de sortie.

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

Si vous utilisez beaucoup de ressources, vous pouvez envisager d'utiliser   Commons VFS .

prend également en charge:  * Fichiers locaux  * FTP, SFTP  * HTTP et HTTPS  * Fichiers temporaires "normal FS sauvegardé)  * Zip, Jar et Tar (non compressé, tgz ou tbz2)  * gzip et bzip2  * Ressources  * ram - " ramdrive "  * mime

Il existe également un JBoss VFS , mais il n’est pas très documenté.

J'ai 2 fichiers CSV que je utilise pour lire les données. Le programme java est exporté sous forme de fichier jar exécutable. Lorsque vous l'exporterez, vous découvrirez qu'il n'exporte pas vos ressources avec celui-ci.

J'ai ajouté un dossier sous projet appelé data in eclipse. 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 String ZIP_FILE_LOCATION_PRIMARY = "free-zipcode-database-Primary.csv";
private static final String ZIP_FILE_LOCATION = "free-zipcode-database.csv";

private static String getFileLocation(){
    String 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;
}

Lorsque vous placez le fichier JAR à un emplacement permettant son exécution en ligne de commande, veillez à ajouter le dossier de données contenant les ressources au même emplacement que le fichier JAR.

Voici un exemple de code expliquant comment lire correctement un fichier dans un fichier jar (dans ce cas, le fichier jar en cours d'exécution)

Modifiez simplement le fichier exécutable avec le chemin de votre fichier jar s'il ne s'agit pas du fichier en cours d'exécution.

Modifiez ensuite filePath en plaçant le chemin du fichier que vous souhaitez utiliser dans le fichier jar. C'EST À DIRE. si votre fichier est dans

  

someJar.jar \ img \ test.gif

. Définissez filePath sur "img \ test.gif"

File executable = new File(BrowserViewControl.class.getProtectionDomain().getCodeSource().getLocation().toURI());
JarFile jar = new JarFile(executable);
InputStream fileInputStreamReader = jar.getInputStream(jar.getJarEntry(filePath));
byte[] bytes = new byte[fileInputStreamReader.available()];

int sizeOrig = fileInputStreamReader.available();
int size = fileInputStreamReader.available();
int offset = 0;
while (size != 0){
    fileInputStreamReader.read(bytes, offset, size);
    offset = sizeOrig - fileInputStreamReader.available();
    size = fileInputStreamReader.available();
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top