Recurso Java como archivo

122

¿Hay alguna manera en Java para construir una instancia de archivo en un recurso recuperado de un jar a través del cargador de clases?

Mi aplicación utiliza algunos archivos del archivo jar (predeterminado) o de un directorio del sistema de archivos especificado en tiempo de ejecución (entrada del usuario). Estoy buscando una forma consistente de
a) cargar estos archivos como una secuencia
b) enumerar los archivos en el directorio definido por el usuario o el directorio en el jar respectivamente

Editar: Aparentemente, el enfoque ideal sería mantenerse alejado de java.io.File por completo. ¿Hay alguna manera de cargar un directorio desde el classpath y enumerar sus contenidos (archivos / entidades contenidos en él)?

Mantrum
fuente

Respuestas:

58

ClassLoader.getResourceAsStreamy Class.getResourceAsStreamson definitivamente el camino a seguir para cargar los datos de recursos. Sin embargo, no creo que haya ninguna forma de "enumerar" el contenido de un elemento del classpath.

En algunos casos, esto puede ser simplemente imposible, por ejemplo, a ClassLoader podría generar datos sobre la marcha, en función del nombre del recurso que se solicita. Si observa la ClassLoaderAPI (que es básicamente a través de la cual funciona el mecanismo classpath) verá que no hay nada que haga lo que desea.

Si sabe que realmente tiene un archivo jar, puede cargarlo ZipInputStreampara averiguar qué hay disponible. Sin embargo, significará que tendrá un código diferente para directorios y archivos jar.

Una alternativa, si los archivos se crean por separado primero, es incluir una especie de archivo de manifiesto que contenga la lista de recursos disponibles. Agrúpelo en el archivo jar o inclúyalo en el sistema de archivos como un archivo y cárguelo antes de ofrecer al usuario una selección de recursos.

Jon Skeet
fuente
3
Estoy de acuerdo en que es molesto, pero hace que ClassLoader sea más ampliamente aplicable de otras maneras. Por ejemplo, es fácil escribir un "cargador de clases web" porque la web es buena para buscar archivos, pero generalmente no enumera los archivos.
Jon Skeet
Parece que si el recurso que está intentando cargar es mucho más grande que 1MiB, InputStreamse obtiene de getResourceAsStreamparadas para recuperar los bytes del recurso después de ese tamaño y en su lugar devuelve 0 si está contenido en un sistema de archivos comprimido como un jar. Parece que se ve obligado a usar getResourcey cargar el archivo independientemente de esto en ese caso.
mgttlinger
1
@mgttlinger: Eso me parece poco probable ... probablemente valga la pena publicar una nueva pregunta con un repro si es posible.
Jon Skeet
El problema se debió al readmétodo que decidió cuántos datos leer (no estaba al tanto de eso). Y parece que todo el archivo se lee si el archivo que se está leyendo se encuentra en una carpeta normal. Si el archivo está dentro de un archivo jar porque ha sido empaquetado, solo lee partes de él.
mgttlinger
1
@mgttlinger: No, no solo leerá partes de él, sino que podría no llenar todo el búfer proporcionado en cada llamada de lectura. Como siempre InputStream, si desea leer todo el recurso, debe repetir hasta que readdevuelva -1.
Jon Skeet
98

Tuve el mismo problema y pude usar lo siguiente:

// Load the directory as a resource
URL dir_url = ClassLoader.getSystemResource(dir_path);
// Turn the resource into a File object
File dir = new File(dir_url.toURI());
// List the directory
String files = dir.list()
Chris Conway
fuente
45
Este enfoque no funciona con JAR en classpath, solo con archivos y directorios
yegor256
1
@ yegor256 Si su archivo está en un frasco, puede crear un FileSystemobjeto y obtenerlo Path. Entonces puedes leerlo como de costumbre. (ver stackoverflow.com/a/22605905/1876344 )
mgttlinger
53

Aquí hay un poco de código de una de mis aplicaciones ... Avíseme si se adapta a sus necesidades. Puede usar esto si conoce el archivo que desea usar.

URL defaultImage = ClassA.class.getResource("/packageA/subPackage/image-name.png");
File imageFile = new File(defaultImage.toURI());

Espero que ayude.

Maurice Rogers
fuente
Sí, sí, pero toURI()fallará.
Olivier Faucheux
10

Una forma confiable de construir una instancia de archivo en un recurso recuperado de un jar es copiar el recurso como una secuencia en un archivo temporal (el archivo temporal se eliminará cuando la JVM salga):

public static File getResourceAsFile(String resourcePath) {
    try {
        InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(resourcePath);
        if (in == null) {
            return null;
        }

        File tempFile = File.createTempFile(String.valueOf(in.hashCode()), ".tmp");
        tempFile.deleteOnExit();

        try (FileOutputStream out = new FileOutputStream(tempFile)) {
            //copy stream
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, bytesRead);
            }
        }
        return tempFile;
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
}
Lukas Masuch
fuente
4

Prueba esto:

ClassLoader.getResourceAsStream ("some/pkg/resource.properties");

Hay más métodos disponibles, por ejemplo, ver aquí: http://www.javaworld.com/javaworld/javaqa/2003-08/01-qa-0808-property.html

el mejor chef
fuente
Gracias por su respuesta. Sé acerca de getResourceAsStream, pero no creo que pueda cargar un directorio desde el classpath a través de eso.
Mantrum
En el directorio Java hay un archivo (raíces UNIX, supongo), así que al menos deberías intentarlo.
topchef
Creo que para enumerar los archivos en un directorio, necesitará usar java.io.File. Puede buscar archivos con ClassLoader.findResource, que devuelve una URL que se puede pasar a Archivo.
Nathan Voxland
2

Esta es una opción: http://www.uofr.net/~greg/java/get-resource-listing.html

Pez
fuente
44
Si bien este enlace puede responder a la pregunta, sería preferible incluir aquí las partes esenciales de la respuesta y proporcionar el enlace como referencia, ya que un enlace puede cambiar o la página enlazada puede eliminarse, haciendo que la respuesta sea inútil.
JonasCz - Restablece a Mónica el