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

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.


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

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.


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

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


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

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.