Astuce Java 49: Comment extraire des ressources Java des archives JAR et zip

La plupart des programmeurs Java sont assez clairs sur les avantages d'utiliser un fichier JAR pour regrouper toutes les diverses ressources (c'est-à-dire les fichiers .class, les sons et les images) qui composent leur solution Java. (Si vous n'êtes pas familiarisé avec les fichiers JAR, consultez la section Ressources ci-dessous.) Une question très courante posée par les personnes qui commencent tout juste à incorporer des fichiers JAR dans leur sac d'astuces est la suivante: "Comment extraire une image d'un POT?" Nous allons répondre à cette question et fournir une classe pour simplifier l'extraction de n'importe quelle ressource d'un JAR!

Chargement d'une image GIF

Disons que nous avons un fichier JAR contenant un tas de fichiers image .gif que nous voulons utiliser dans notre application. Voici comment nous pourrions accéder à un fichier image à partir du JAR en utilisant JarResources:

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

Cet extrait de code montre que nous pouvons créer un JarResourcesobjet initialisé dans le fichier JAR contenant la ressource que nous souhaitons utiliser - Images.jar. Nous utilisons ensuite la JarResources'getResource()méthode pour fournir les données brutes du fichier logo.gif pour la createImage()méthode de la boîte à outils AWT .

Une note sur la dénomination

JarResource est un exemple raisonnablement simple d'utilisation des diverses fonctionnalités fournies par Java 1.1 pour manipuler les fichiers d'archive JAR et zip.

Une note rapide sur la dénomination. La prise en charge de l'archivage en Java a en fait commencé en utilisant le format d'archivage zip populaire (consultez «Astuce Java 21: Utilisez des fichiers d'archive pour accélérer le chargement des applets»). Ainsi, à l'origine, lors de l'implémentation du support Java pour manipuler les fichiers d'archive, toutes les classes et autres ont été placées dans le package java.util.zip; ces classes ont tendance à commencer par " Zip." Mais quelque part dans le passage à Java 1.1, les pouvoirs qui ont changé le nom de l'archive pour être plus axé sur Java. Par conséquent, ce que nous appelons maintenant les fichiers JAR sont essentiellement des fichiers zip.

Comment ça fonctionne

Les champs de données importants pour la JarResourcesclasse sont utilisés pour suivre et stocker le contenu du fichier JAR spécifié:

Classe finale publique JarResources {public boolean debugOn = false; Hashtable privé htSizes = new Hashtable (); Hashtable privé htJarContents = new Hashtable (); private String jarFileName;

Ainsi, l'instanciation de la classe définit le nom du fichier JAR, puis appelle la init()méthode pour faire tout le vrai travail:

public JarResources (String jarFileName) {this.jarFileName = jarFileName; init (); }

Maintenant, la init()méthode charge à peu près tout le contenu du fichier JAR spécifié dans une table de hachage (accessible via le nom de la ressource).

C'est une méthode assez lourde, alors décomposons-la un peu plus loin. La ZipFileclasse nous donne un accès de base aux informations d'en-tête de l'archive JAR / zip. Ceci est similaire aux informations de répertoire dans un système de fichiers. Ici, nous énumérons toutes les entrées du ZipFileet construisons la table de hachage htSizes avec la taille de chaque ressource dans l'archive:

private void init () {try {ZipFile zf = new ZipFile (jarFileName); Énumération 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 ();

Ensuite, nous accédons à l'archive via l'utilisation de la ZipInputStreamclasse. La ZipInputStreamclasse fait toute la magie pour nous permettre de lire chacune des ressources individuelles de l'archive. Nous lisons le nombre exact d'octets de l'archive qui composent chaque ressource et stockons ces données dans la table de hachage htJarContents accessible par nom de ressource:

FileInputStream fis = new FileInputStream (jarFileName); BufferedInputStream bis = nouveau BufferedInputStream (fis); ZipInputStream zis = nouveau ZipInputStream (bis); ZipEntry ze = null; while ((ze = zis.getNextEntry ())! = null) {if (ze.isDirectory ()) {continue; } if (debugOn) {System.out.println ("ze.getName () =" + ze.getName () + "," + "getSize () =" + ze.getSize ()); } int size = (int) ze.getSize (); // -1 signifie une taille inconnue. if (size == - 1) {size = ((Integer) htSizes.get (ze.getName ())). intValue (); } octet [] b = nouvel octet [taille (int)]; int rb = 0; bloc int = 0; while (((int) size - rb)> 0) {chunk = zis.read (b, rb, (int) size - rb); if (morceau == - 1) {pause; } rb + = bloc; } // ajouter à la table de hachage de la ressource interne 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 ("done."); } catch (FileNotFoundException e) {e.printStackTrace (); } catch (IOException e) {e.printStackTrace (); }}

Notez que le nom utilisé pour identifier chaque ressource est le nom de chemin qualifié de la ressource dans l'archive, et non , par exemple, le nom d'une classe dans un package - c'est-à-dire que la ZipEntryclasse du package java.util.zip être nommé «java / util / zip / ZipEntry» plutôt que «java.util.zip.ZipEntry».

La dernière partie importante du code est le pilote de test simple. Le pilote de test est une application simple qui prend un nom d'archive JAR / zip et le nom d'une ressource. Il essaie de trouver la ressource dans l'archive et signale son succès ou son échec:

public static void main (String [] args) jette IOException {if (args.length! = 2) {System.err.println ("usage: java JarResources"); System.exit (1); } JarResources jr = new JarResources (args [0]); byte [] buff = jr.getResource (args [1]); if (buff == null) {System.out.println ("Impossible de trouver" + args [1] + "."); } else {System.out.println ("Trouvé" + args [1] + "(longueur =" + buff.length + ")."); }}} // Fin de la classe JarResources.

Et voila. Une classe simple à utiliser qui cache tout le désordre lié à l'utilisation de ressources cachées dans des fichiers JAR.

Exercices pour le lecteur

Maintenant que vous avez une idée de l'extraction de ressources à partir d'un fichier d'archive, voici quelques directions que vous voudrez peut-être explorer pour modifier et étendre la JarResourcesclasse:

  • Au lieu de tout charger pendant la construction, effectuez un chargement différé. Dans le cas d'un gros fichier JAR, il se peut qu'il n'y ait pas assez de mémoire pour charger tous les fichiers pendant la construction.
  • Au lieu de simplement fournir une méthode d'accesseur générique comme getResource(), nous pourrions fournir d'autres accesseurs spécifiques aux ressources - par exemple getImage(),, qui renvoie un Imageobjet Java ,, getClass()qui renvoie un Classobjet Java (avec l'aide d'un chargeur de classe personnalisé), et ainsi de suite. Si le fichier JAR est suffisamment petit, nous pourrions pré-construire toutes les ressources en fonction de leurs extensions (.gif, .class, etc.).
  • Some methods should provide information about the given JAR file itself (basically a wrapper around ZipFile), including: the number of Jar/zip entries; an enumerator that returns all the names of resources; accessors that return the length (and other attributes) of a particular entry; and an accessor that allows indexing, to name a few.
  • JarResources can be extended to be used by applets. By utilizing applet parameters and the URLConnection class, the JAR content can be downloaded from the network instead of opening the archives as local files. Furthermore, we may extend this class as a custom Java content handler.

Conclusion

If you've been eager to know how to extract an image from a JAR file, now you've got a way. Not only can you handle images with a JAR file, but with the new class provided in this tip, you work your extracting magic on any resource from a JAR.

Arthur Choi travaille actuellement pour IBM en tant que programmeur conseil. Il a travaillé pour plusieurs entreprises, dont SamSung Network Laboratory et MITRE. Les différents projets sur lesquels il a travaillé sont les systèmes client / serveur, l'informatique d'objets distribués et la gestion de réseau. Il a utilisé un certain nombre de langues dans divers environnements de système d'exploitation. Il a commencé la programmation en 1981 avec FORTRAN IV et COBOL. Plus tard, il est passé au C et au C ++, et il travaille avec Java depuis environ deux ans. Il s'intéresse surtout aux applications de Java dans les domaines des référentiels de données via des réseaux étendus et du traitement parallèle et distribué via Internet (utilisant la programmation basée sur des agents). John Mitchell, employé, consultant et directeur de sa propre entreprise, a investi ces dix dernières années dans le développement de logiciels informatiques de pointe,et en conseillant et en formant d'autres développeurs. Il a fourni des conseils sur la technologie Java, les compilateurs, les interprètes, les applications Web et le commerce Internet. John a co-écrit Making Sense of Java: A Guide for Managers and the Rest of Us et a publié des articles dans des revues de programmation. En plus d'écrire la colonne Java Tips pour JavaWorld, il anime les groupes de discussion comp.lang.tcl.announce et comp.binaries.geos.

En savoir plus sur ce sujet

  • Voici le fichier de classe JarResources.java//www.javaworld.com/javatips/javatip49/JarResources.java
  • JAR //www.javasoft.com/products/jdk/1.1/docs/guide/jar/index.html
  • Pour plus d'informations sur la prise en charge de l'archivage en Java, consultez «Astuce Java 21Utiliser des fichiers d'archive pour accélérer le chargement des applets» //www.javaworld.com/javatips/jw-javatip21.html

Cette histoire, «Java Tip 49: Comment extraire des ressources Java à partir d'archives JAR et zip» a été publiée à l'origine par JavaWorld.