¿Cómo puedo obtener una "Carpeta" de recursos desde mi archivo jar?

139

Tengo una carpeta / paquete de recursos en la raíz de mi proyecto, "no" quiero cargar un determinado archivo. ¡Si quisiera cargar un determinado archivo, usaría class.getResourceAsStream y estaría bien! Lo que realmente quiero hacer es cargar una "Carpeta" dentro de la carpeta de recursos, hacer un bucle en los Archivos dentro de esa Carpeta y obtener una Secuencia para cada archivo y leer en el contenido ... Suponga que los nombres de los Archivos no están determinados antes del tiempo de ejecución ... ¿Qué tengo que hacer? ¿Hay alguna manera de obtener una lista de los archivos dentro de una carpeta en su archivo jar? Observe que el archivo Jar con los recursos es el mismo archivo jar desde el que se ejecuta el código ...

Gracias por adelantado...

Mostafa Zeinali
fuente
1
Esto ayudaría: stackoverflow.com/questions/1529611/…
Jigar Joshi
Ver también: stackoverflow.com/questions/4764347/…
Jigar Joshi
55
¡El primero trata de extraer un archivo jar, que es lo último que quiero probar, como una situación de peor a peor! Si quiero que se extraigan los archivos, ¡simplemente los pondría en mi carpeta de instalación en el momento de la instalación! Quiero acceder a ellos desde dentro del jar, y mi razón es que si getResourceAsStream puede acceder a un archivo, ¡Java también debería poder explorar carpetas en ese jar, sin la necesidad de extraerlo! Pero gracias ...
Mostafa Zeinali

Respuestas:

111

Finalmente, encontré la solución:

final String path = "sample/folder";
final File jarFile = new File(getClass().getProtectionDomain().getCodeSource().getLocation().getPath());

if(jarFile.isFile()) {  // Run with JAR file
    final JarFile jar = new JarFile(jarFile);
    final Enumeration<JarEntry> entries = jar.entries(); //gives ALL entries in jar
    while(entries.hasMoreElements()) {
        final String name = entries.nextElement().getName();
        if (name.startsWith(path + "/")) { //filter according to the path
            System.out.println(name);
        }
    }
    jar.close();
} else { // Run with IDE
    final URL url = Launcher.class.getResource("/" + path);
    if (url != null) {
        try {
            final File apps = new File(url.toURI());
            for (File app : apps.listFiles()) {
                System.out.println(app);
            }
        } catch (URISyntaxException ex) {
            // never happens
        }
    }
}

El segundo bloque simplemente funciona cuando ejecuta la aplicación en IDE (no con el archivo jar). Puede eliminarlo si no le gusta.

usuario1079877
fuente
Se actualizó la respuesta correcta. Aunque esto no es lo que me gustaría hacer, especialmente en un código con más de 34000 archivos fuente ... Pero parece ser la única solución. Gracias.
Mostafa Zeinali
44
Para su información, esto no funciona si se inicia desde webstart porque getPath devuelve algo relativo al dominio del servidor.
amos
1
No piense que va a funcionar si la ruta está en un frasco, dará este error en la conversión toURI: java.lang.IllegalArgumentException: URI no es jerárquico
tribbloid
44
Solo funciona para archivos de un solo jar, no funciona para extraer recursos de dependencias en otro jar
tribbloid
2
Esta no es una solución segura porque: 1. Asume que el archivo .jar es un archivo local en el mismo sistema; 2. URL.getPath () no devuelve un nombre de archivo válido, solo devuelve la porción de ruta de la URL, con todos los porcentajes de escape intactos; 3. ProtectionDomain.getCodeSource () puede devolver nulo .
VGR
14

Intenta lo siguiente.
Cree la ruta del recurso "<PathRelativeToThisClassFile>/<ResourceDirectory>" Por ejemplo, si su ruta de clase es com.abc.package.MyClass y sus archivos de recursos están dentro de src / com / abc / package / resources /:

URL url = MyClass.class.getResource("resources/");
if (url == null) {
     // error - missing folder
} else {
    File dir = new File(url.toURI());
    for (File nextFile : dir.listFiles()) {
        // Do something with nextFile
    }
}

También puedes usar

URL url = MyClass.class.getResource("/com/abc/package/resources/");
Glen Best
fuente
27
Gracias por el tiempo y el esfuerzo, pero esto NO funciona cuando su código se basa en un archivo .jar y sus recursos también están en ese archivo .jar. Cuando está dentro de un archivo .jar, no puede crear un Archivo () y tampoco puede obtener una URL para esa carpeta ... Personalmente me sentí tranquilo y solo enumeré cada archivo en un XML ... No lo hago ' No creo que pueda hacer eso para una carpeta dentro de un archivo jar ...
Mostafa Zeinali
2
Lamento que estés teniendo problemas. Sin embargo, en realidad funciona cuando su base de código está en un archivo jar y sus recursos también están en ese archivo jar. No está creando un archivo () en el disco; está leyendo el archivo dentro del archivo jar en la memoria java. Tenga en cuenta que ClassLoader se complace en tratar un archivo jar equivalente a un directorio en el disco. Aquí están los detalles de la fuente:
Glen Best
1
Simplemente ejecuté el código en un jar y obtuve el error ... ¡Vaya! V lo siento, mi mal. Necesita manejar la ruta URL del jar con "File: ...! / Dir1 / dir2 ...". Dos soluciones: stackoverflow.com/questions/1429172/… O String jarPath = dirURL.getPath (). Substring (5, dirURL.getPath (). IndexOf ("!")); JarFile jar = nuevo JarFile (URLDecoder.decode (jarPath, "UTF-8")); Enumeración <JarEntry> entradas = jar.entries (); while (entries.hasMoreElements ()) {// haz algo}
Glen Best el
3
Desde el javadoc es obvio que el método NO SIEMPRE está garantizado para devolver un archivo en todas las circunstancias. PERO, si realmente lee la Q, el OP está 100% garantizado para trabajar con archivos y carpetas por construcción. La Q solicita acceso a directorios y archivos; esto es más específico que la abstracción generalizada de la API. La conversión a archivo es apropiada y correcta para satisfacer la necesidad. Lea la Q correctamente antes de publicar comentarios tan contrarios. Salud.
Glen Best
77
El OP no funciona con archivos y directorios. Está trabajando con recursos en un archivo JAR. Los recursos en un archivo JAR no son archivos o directorios y no se pueden enumerar con la Fileclase. Este código no funciona con recursos dentro de un archivo JAR. Período.
Marqués de Lorne
7

Sé que esto es hace muchos años. Pero solo para otras personas se encuentran con este tema. Lo que podría hacer es usar el getResourceAsStream()método con la ruta del directorio, y la secuencia de entrada tendrá todos los nombres de archivo de ese directorio. Después de eso, puede concatenar la ruta del directorio con cada nombre de archivo y llamar a getResourceAsStream para cada archivo en un bucle.

Cheng Lin
fuente
77
Esto funciona bien a menos que planees sacudir las cosas, como resulta.
Tustin2121
1
Creo que esta debería ser la solución aceptada, ya que está limpia y no requiere el manejo de las versiones jar e IDE en casos separados. Aunque no estoy seguro de por qué getResource no encuentra la carpeta, pero getResourceAsStream sí.
Raghuram Onti Srinivasan
6

Tuve el mismo problema cuando intentaba cargar algunas configuraciones de hadoop de los recursos empaquetados en el jar ... tanto en el IDE como en el jar (versión de lanzamiento).

Descubrí java.nio.file.DirectoryStreamque funciona mejor para iterar sobre el contenido del directorio tanto en el sistema de archivos local como en el jar.

String fooFolder = "/foo/folder";
....

ClassLoader classLoader = foofClass.class.getClassLoader();
try {
    uri = classLoader.getResource(fooFolder).toURI();
} catch (URISyntaxException e) {
    throw new FooException(e.getMessage());
} catch (NullPointerException e){
    throw new FooException(e.getMessage());
}

if(uri == null){
    throw new FooException("something is wrong directory or files missing");
}

/** i want to know if i am inside the jar or working on the IDE*/
if(uri.getScheme().contains("jar")){
    /** jar case */
    try{
        URL jar = FooClass.class.getProtectionDomain().getCodeSource().getLocation();
        //jar.toString() begins with file:
        //i want to trim it out...
        Path jarFile = Paths.get(jar.toString().substring("file:".length()));
        FileSystem fs = FileSystems.newFileSystem(jarFile, null);
        DirectoryStream<Path> directoryStream = Files.newDirectoryStream(fs.getPath(fooFolder));
        for(Path p: directoryStream){
            InputStream is = FooClass.class.getResourceAsStream(p.toString()) ;
        performFooOverInputStream(is);
        /** your logic here **/
            }
    }catch(IOException e) {
        throw new FooException(e.getMessage());     
    }
}
else{
    /** IDE case */
    Path path = Paths.get(uri);
    try {
        DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path);
        for(Path p : directoryStream){
            InputStream is = new FileInputStream(p.toFile());
            performFooOverInputStream(is);
        }
    } catch (IOException _e) {
        throw new FooException(_e.getMessage());
    }
}
Inyección SQL
fuente
Esto funcionó bien para mi. Solo cambié 'Path jarFile = Paths.get (jar.toString (). Substring ("file:". Length ()));' con 'Ruta jarFile = Paths.get (jar.toURI ());' para que funcione en Windows. También se utilizó una declaración de prueba con recursos para el objeto FileSystem.
Jarle Jacobsen
¡Esto funcionó para mí! Estaba ejecutando la aplicación dropwizard en Docker.
nIKUNJ
4

El siguiente código devuelve la "carpeta" deseada como Ruta, independientemente de si está dentro de un jar o no.

  private Path getFolderPath() throws URISyntaxException, IOException {
    URI uri = getClass().getClassLoader().getResource("folder").toURI();
    if ("jar".equals(uri.getScheme())) {
      FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap(), null);
      return fileSystem.getPath("path/to/folder/inside/jar");
    } else {
      return Paths.get(uri);
    }
  }

Requiere java 7+.

tarareando
fuente
¡Agradable! Sin embargo, debo agregar que FileSystem es Closeable, lo que significa que debe cerrarlo en algún momento para liberar recursos del sistema. Por supuesto, la ruta devuelta se invalidará inmediatamente después de eso.
Kirill Gamazkov
Tenga en cuenta que en el caso de un archivo jar, el nombre del recurso (aquí paquete + folder) parece ignorarse al crear el sistema de archivos. Por getPathlo tanto no regresa folder/path/to/..., sino solo path/to/....
Marcono1234
1

Otra solución, puedes hacerlo usando ResourceLoaderasí:

import org.springframework.core.io.Resource;
import org.apache.commons.io.FileUtils;

@Autowire
private ResourceLoader resourceLoader;

...

Resource resource = resourceLoader.getResource("classpath:/path/to/you/dir");
File file = resource.getFile();
Iterator<File> fi = FileUtils.iterateFiles(file, null, true);
while(fi.hasNext()) {
    load(fi.next())
}
Qy Zuo
fuente
0

Simple ... usa OSGi. En OSGi puede iterar sobre las entradas de su paquete con findEntries y findPaths.

Peter Kriens
fuente
10
Si involucra OSGI, difícilmente lo llamaría 'simple'. Pero si ya estás usando OSGI ... sí, claro. Pero sugerir pasar a OSGI solo para resolver este problema en particular parece demasiado.
Kris
OSGi también resuelve muchos otros problemas y hoy en día es muy fácil comenzar. Vea los tutoriales de OSGi enRoute
Peter Kriens, el
Me mantendría alejado de OSGi a menos que ya esté obligado a usarlo. Bloatware sobre diseñado que resuelve los problemas de implementación de la IMO de los 90.
user2337270
-1

Dentro de mi archivo jar tenía una carpeta llamada Cargar, esta carpeta tenía otros tres archivos de texto dentro y necesitaba tener exactamente la misma carpeta y archivos fuera del archivo jar, utilicé el siguiente código:

URL inputUrl = getClass().getResource("/upload/blabla1.txt");
File dest1 = new File("upload/blabla1.txt");
FileUtils.copyURLToFile(inputUrl, dest1);

URL inputUrl2 = getClass().getResource("/upload/blabla2.txt");
File dest2 = new File("upload/blabla2.txt");
FileUtils.copyURLToFile(inputUrl2, dest2);

URL inputUrl3 = getClass().getResource("/upload/blabla3.txt");
File dest3 = new File("upload/Bblabla3.txt");
FileUtils.copyURLToFile(inputUrl3, dest3);
LoveLovelyJava
fuente
1
Hola, gracias por la respuesta, pero ya ves, necesitabas saber el nombre de cada archivo dentro de esa carpeta al escribir el código; y tenías que nombrar a cada uno explícitamente. Lo que estaba buscando era un método para iterar en los archivos dentro de la carpeta y obtener sus nombres en tiempo de ejecución, para poder agregar recursos a la carpeta, sin cambiar el código, sabiendo que el código también los procesará. Gracias por tu tiempo sin embargo. : 3
Mostafa Zeinali
-1

Como señalan las otras respuestas, una vez que los recursos están dentro de un archivo jar, las cosas se ponen realmente feas. En nuestro caso, esta solución:

https://stackoverflow.com/a/13227570/516188

funciona muy bien en las pruebas (ya que cuando se ejecutan las pruebas, el código no está empaquetado en un archivo jar), pero no funciona cuando la aplicación realmente se ejecuta normalmente. Entonces, lo que he hecho es ... codifico la lista de archivos en la aplicación, pero tengo una prueba que lee la lista real del disco (puedo hacerlo ya que funciona en las pruebas) y falla si la lista real no No coincide con la lista que devuelve la aplicación.

De esa manera tengo un código simple en mi aplicación (sin trucos), y estoy seguro de que no olvidé agregar una nueva entrada en la lista gracias a la prueba.

Emmanuel Touzery
fuente
-25

Este enlace te dice cómo.

La magia es el método getResourceAsStream ():

InputStream is = 
this.getClass().getClassLoader().getResourceAsStream("yourpackage/mypackage/myfile.xml")
James Anderson
fuente
20
¡¡No amigo!! ¡NO quiero un solo archivo! ¡Quiero una Carpeta entera, cuyos sub archivos son indeterminados antes del tiempo de ejecución!
Mostafa Zeinali