Pregunta

Estoy intentando acceder a un archivo XML dentro de un archivo jar, desde un jar separado que se ejecuta como una aplicación de escritorio. Puedo obtener la URL del archivo que necesito, pero cuando lo paso a un FileReader (como String) obtengo una excepción FileNotFoundException que dice: "El nombre del archivo, el nombre del directorio o la etiqueta del volumen son incorrectos. & Quot;

Como punto de referencia, no tengo problemas para leer los recursos de imágenes del mismo archivo, pasando la URL a un constructor de ImageIcon. Esto parece indicar que el método que estoy usando para obtener la URL es correcto.

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

Dentro de la clase ServicesLoader que tengo

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

¿Qué tiene de malo usar esta técnica para leer el archivo XML?

¿Fue útil?

Solución 4

El problema fue que iba un paso demasiado lejos al llamar al método de análisis de XMLReader. El método de análisis acepta un InputSource, por lo que no había ninguna razón para utilizar un FileReader. Cambiando la última línea del código de arriba a

xr.parse( new InputSource( filename ));

funciona bien.

Otros consejos

Parece que desea utilizar java.lang.Class.getResourceAsStream (String) , consulte

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

No dices si se trata de una aplicación de escritorio o web. Usaría el método getResourceAsStream () de un ClassLoader apropiado si es un escritorio o el Context si es una aplicación web.

Parece que está utilizando el resultado URL.toString como argumento para el constructor FileReader . URL.toString está un poco dañado, y en su lugar, generalmente debe utilizar url.toURI (). toString () . En cualquier caso, la cadena no es una ruta de archivo.

En su lugar, deberías:

  • Pase el URL a ServicesLoader y deje que llame a openStream o similar.
  • Use Class.getResourceAsStream y simplemente pase la secuencia, posiblemente dentro de un InputSource . (Recuerda verificar los nulos ya que la API es un poco desordenada).

Me gustaría señalar que uno de los problemas es qué sucede si los mismos recursos se encuentran en varios archivos jar. Digamos que desea leer /org/node/foo.txt, pero no de un archivo, sino de todos y cada uno de los archivos jar.

Me he encontrado con este mismo problema varias veces antes. Esperaba en el JDK 7 que alguien escribiera un sistema de archivos classpath, pero lamentablemente todavía no.

Spring tiene la clase de recursos que te permite cargar los recursos de classpath bastante bien.

Escribí un pequeño prototipo para resolver este problema de leer recursos desde múltiples archivos jar. El prototipo no maneja todos los casos de borde, pero sí busca recursos en directorios que están en los archivos jar.

He usado Stack Overflow por bastante tiempo. Esta es la segunda respuesta que recuerdo haber respondido a una pregunta, así que perdóneme si paso demasiado tiempo (es mi naturaleza).

Este es un prototipo de lector de recursos. El prototipo carece de una comprobación de errores robusta.

Tengo dos archivos jar prototipo que tengo configurados.

 <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>

Cada uno de los archivos jar tiene un archivo en / org / node / denominado resource.txt.

Esto es solo un prototipo de cómo se vería un manejador con classpath: // También tengo un resource.foo.txt en mis recursos locales para este proyecto.

Los recoge a todos y los 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();
                    }

                }

            }
        }

    }


   

Puede ver un ejemplo más completo aquí con la salida de muestra.

Fuera de su técnica, ¿por qué no utilizar el estándar de Java? clase JarFile para obtener las referencias que desea? A partir de ahí, la mayoría de tus problemas deberían desaparecer.

Si usa recursos extensivamente, podría considerar usar   Commons VFS .

También soporta:  * Archivos locales  * FTP, SFTP  * HTTP y HTTPS  * Archivos temporales " respaldo normal del FS)  * Zip, Jar y Tar (sin comprimir, tgz o tbz2)  * gzip y bzip2  * recursos  * ram - " ramdrive "  * mimo

También hay JBoss VFS , pero no está muy documentado.

Tengo 2 archivos CSV que utilizo para leer datos. El programa java se exporta como un archivo jar ejecutable. Cuando lo exporte, descubrirá que no exporta sus recursos con él.

Agregué una carpeta en el proyecto llamada datos en eclipse. En esa carpeta almacené mis archivos csv.

Cuando necesito hacer referencia a esos archivos, lo hago así ...

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;
}

Luego, cuando coloque el archivo jar en una ubicación para que se pueda ejecutar a través de la línea de comandos, asegúrese de agregar la carpeta de datos con los recursos en la misma ubicación que el archivo jar.

Aquí hay un código de ejemplo sobre cómo leer un archivo correctamente dentro de un archivo jar (en este caso, el archivo jar en ejecución actual)

Solo cambia el archivo ejecutable con la ruta de tu archivo jar si no es el que se está ejecutando actualmente.

Luego, cambie filePath a la ruta del archivo que desea utilizar dentro del archivo jar. ES DECIR. si tu archivo está en

  

someJar.jar \ img \ test.gif

. Establezca filePath en " 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();
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top