Consejo 49 de Java: Cómo extraer recursos Java de archivos JAR y zip

La mayoría de los programadores de Java tienen claras las ventajas de utilizar un archivo JAR para agrupar todos los recursos (es decir, archivos .class, sonidos e imágenes) que componen su solución Java. (Si no está familiarizado con los archivos JAR, consulte la sección Recursos a continuación). Una pregunta muy común que hacen las personas que recién están comenzando a incorporar archivos JAR en su bolsa de trucos es: "¿Cómo extraigo una imagen de un ¿TARRO?" ¡Responderemos esa pregunta y proporcionaremos una clase para que sea muy simple extraer cualquier recurso de un JAR!

Cargando una imagen GIF

Digamos que tenemos un archivo JAR que contiene un montón de archivos de imagen .gif que queremos usar en nuestra aplicación. Así es como podríamos acceder a un archivo de imagen desde el JAR usando JarResources:

JarResources jar = new JarResources ("Images.jar"); Image logo = Toolkit.getDefaultToolkit (). CreateImage (jar.getResource ("logo.gif");

Ese fragmento de código muestra que podemos crear un JarResourcesobjeto inicializado en el archivo JAR que contiene el recurso que estamos interesados ​​en usar - Images.jar. Luego usamos el JarResources'getResource()método para proporcionar los datos sin procesar del archivo logo.gif para el createImage()método del kit de herramientas de AWT .

Una nota sobre nombrar

JarResource es un ejemplo razonablemente sencillo de cómo utilizar varias funciones proporcionadas por Java 1.1 para manipular archivos JAR y zip.

Una nota rápida sobre los nombres. El soporte de archivado en Java en realidad comenzó con el popular formato de archivado zip (consulte el "Consejo 21 de Java: utilice archivos de almacenamiento para acelerar la carga de subprogramas"). Entonces, originalmente, al implementar el soporte de Java para manipular los archivos de almacenamiento, todas las clases y demás se colocaron en el paquete java.util.zip; estas clases tienden a comenzar con " Zip." Pero en algún momento del cambio a Java 1.1, los poderes que cambiaron el nombre del archivo para que se centrara más en Java. Por lo tanto, lo que ahora llamamos archivos JAR básicamente son archivos zip.

Cómo funciona

Los campos de datos importantes para la JarResourcesclase se utilizan para rastrear y almacenar el contenido del archivo JAR especificado:

JarResources public final class {public boolean debugOn = false; private Hashtable htSizes = new Hashtable (); Hashtable privado htJarContents = new Hashtable (); private String jarFileName;

Entonces, la instanciación de la clase establece el nombre del archivo JAR y luego llama al init()método para hacer todo el trabajo real:

JarResources públicos (String jarFileName) {this.jarFileName = jarFileName; en eso(); }

Ahora, el init()método prácticamente solo carga todo el contenido del archivo JAR especificado en una tabla hash (a la que se accede mediante el nombre del recurso).

Este es un método bastante pesado, así que analicémoslo un poco más. La ZipFileclase nos da acceso básico a la información del encabezado del archivo JAR / zip. Esto es similar a la información de directorio en un sistema de archivos. Aquí enumeramos todas las entradas en ZipFiley construimos la tabla hash htSizes con el tamaño de cada recurso en el archivo:

private void init () {prueba {ZipFile zf = new ZipFile (jarFileName); Enumeración e = zf.entries (); while (e.hasMoreElements ()) {ZipEntry ze = (ZipEntry) e.nextElement (); if (debugOn) {System.out.println (dumpZipEntry (ze)); } htSizes.put (ze.getName (), new Integer ((int) ze.getSize ())); } zf.close ();

A continuación, accedemos al archivo mediante el uso de la ZipInputStreamclase. La ZipInputStreamclase hace toda la magia para permitirnos leer cada uno de los recursos individuales en el archivo. Leemos el número exacto de bytes del archivo que componen cada recurso y almacenamos esos datos en la tabla hash htJarContents accesible por nombre de recurso:

FileInputStream fis = new FileInputStream (jarFileName); BufferedInputStream bis = nuevo BufferedInputStream (fis); ZipInputStream zis = nuevo ZipInputStream (bis); ZipEntry ze = null; while ((ze = zis.getNextEntry ())! = null) {if (ze.isDirectory ()) {continuar; } if (debugOn) {System.out.println ("ze.getName () =" + ze.getName () + "," + "getSize () =" + ze.getSize ()); } int tamaño = (int) ze.getSize (); // -1 significa tamaño desconocido. if (tamaño == - 1) {tamaño = ((Entero) htSizes.get (ze.getName ())). intValue (); } byte [] b = nuevo byte [(int) tamaño]; int rb = 0; int chunk = 0; while (((int) tamaño - rb)> 0) {trozo = zis.read (b, rb, (int) tamaño - rb); if (chunk == - 1) {romper; } rb + = fragmento; } // agregar a la tabla hash de recursos internos htJarContents.put (ze.getName (), b); if (debugOn) {System.out.println (ze.getName () + "rb =" + rb + ", size =" + size + ", csize =" + ze.getCompressedSize ()); }}} catch (NullPointerException e) {System.out.println ("hecho"); } captura (FileNotFoundException e) {e.printStackTrace (); } captura (IOException e) {e.printStackTrace (); }}

Tenga en cuenta que el nombre que se utiliza para identificar cada recurso es el nombre de la ruta calificada del recurso en el archivo, no , por ejemplo, el nombre de una clase en un paquete, es decir, la ZipEntryclase del paquete java.util.zip llamarse "java / util / zip / ZipEntry", en lugar de "java.util.zip.ZipEntry".

La última parte importante del código es el controlador de prueba simple. El controlador de prueba es una aplicación simple que toma un nombre de archivo JAR / zip y el nombre de un recurso. Intenta encontrar el recurso en el archivo e informa su éxito o fracaso:

public static void main (String [] args) lanza IOException {if (args.length! = 2) {System.err.println ("uso: java JarResources"); System.exit (1); } JarResources jr = new JarResources (args [0]); byte [] buff = jr.getResource (args [1]); if (buff == null) {System.out.println ("No se pudo encontrar" + args [1] + "."); } else {System.out.println ("Encontrado" + args [1] + "(longitud =" + buff.length + ")."); }}} // Fin de la clase JarResources.

Y ahí lo tienes. Una clase fácil de usar que oculta todo el desorden relacionado con el uso de recursos escondidos en archivos JAR.

Ejercicios para el lector

Ahora que tiene una idea de cómo extraer recursos de un archivo de almacenamiento, aquí hay algunas instrucciones que puede querer explorar para modificar y extender la JarResourcesclase:

  • En lugar de cargar todo durante la construcción, realice una carga retrasada. En el caso de un archivo JAR grande, es posible que no haya suficiente memoria para cargar todos los archivos durante la construcción.
  • En lugar de simplemente proporcionar un método de acceso genérico como getResource(), podríamos proporcionar otros accesores específicos de recursos, por ejemplo, getImage()que devuelve un Imageobjeto Java getClass(), que devuelve un Classobjeto Java (con la ayuda de un cargador de clases personalizado), y así sucesivamente. Si el archivo JAR es lo suficientemente pequeño, podríamos preconstruir todos los recursos en función de sus extensiones (.gif, .class, etc.).
  • Algunos métodos deberían proporcionar información sobre el archivo JAR dado en sí (básicamente un contenedor ZipFile), incluyendo: el número de entradas Jar / zip; un enumerador que devuelve todos los nombres de los recursos; descriptores de acceso que devuelven la longitud (y otros atributos) de una entrada en particular; y un descriptor de acceso que permite la indexación, por nombrar algunos.
  • JarResourcesse puede ampliar para que lo utilicen los applets. Al utilizar los parámetros del subprograma y la URLConnectionclase, el contenido JAR se puede descargar de la red en lugar de abrir los archivos como archivos locales. Además, podemos ampliar esta clase como un controlador de contenido Java personalizado.

Conclusión

Si ha estado ansioso por saber cómo extraer una imagen de un archivo JAR, ahora tiene una manera. No solo puede manejar imágenes con un archivo JAR, sino que con la nueva clase proporcionada en este consejo, trabaja su magia de extracción en cualquier recurso de un JAR.

Arthur Choi trabaja actualmente para IBM como programador asesor. Ha trabajado para varias empresas, incluidas SamSung Network Laboratory y MITRE. Los diversos proyectos en los que ha trabajado son sistemas cliente / servidor, computación de objetos distribuidos y gestión de redes. Ha utilizado varios idiomas en varios entornos de sistemas operativos. Comenzó a programar en 1981 con FORTRAN IV y COBOL. Más tarde, cambió a C y C ++, y ha estado trabajando con Java durante aproximadamente dos años. Está más interesado en aplicaciones de Java en las áreas de repositorios de datos a través de redes de área amplia y procesamiento paralelo y distribuido a través de Internet (usando programación basada en agentes). John Mitchell, empleado, consultor y director de su propia empresa, ha invertido los últimos diez años en el desarrollo de software informático de última generación,y en asesorar y capacitar a otros desarrolladores. Ha brindado consultoría sobre tecnología Java, compiladores, intérpretes, aplicaciones basadas en la Web y comercio por Internet. John es coautor de Making Sense of Java: A Guide for Managers and the Rest of Us y ha publicado artículos en revistas de programación. Además de escribir la columna Java Tips para JavaWorld, modera los grupos de noticias comp.lang.tcl.announce y comp.binaries.geos.

Más información sobre este tema

  • Aquí está el archivo de clase JarResources.java//www.javaworld.com/javatips/javatip49/JarResources.java
  • JAR //www.javasoft.com/products/jdk/1.1/docs/guide/jar/index.html
  • Para obtener más información sobre la compatibilidad con el archivo en Java, consulte "Consejo 21 de Java: utilice archivos de almacenamiento para acelerar la carga de subprogramas" //www.javaworld.com/javatips/jw-javatip21.html

Esta historia, "Consejo 49 de Java: Cómo extraer recursos Java de archivos JAR y zip" fue publicada originalmente por JavaWorld.