Suggerimento Java 49: come estrarre risorse Java da archivi JAR e zip

La maggior parte dei programmatori Java è abbastanza chiara sui vantaggi dell'utilizzo di un file JAR per raggruppare tutte le varie risorse (cioè file .class, suoni e immagini) che compongono la loro soluzione Java. (Se non hai familiarità con i file JAR, consulta la sezione Risorse di seguito.) Una domanda molto comune posta da persone che stanno appena iniziando a incorporare file JAR nella loro borsa di trucchi è: "Come estrarre un'immagine da VASO?" Risponderemo a questa domanda e forniremo una classe per semplificare l'estrazione di qualsiasi risorsa da un JAR!

Caricamento di un'immagine GIF

Supponiamo di avere un file JAR contenente un gruppo di file di immagine .gif che vogliamo utilizzare nella nostra applicazione. Ecco come possiamo accedere a un file immagine dal JAR utilizzando JarResources:

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

Lo snippet di codice mostra che possiamo creare un JarResourcesoggetto inizializzato nel file JAR contenente la risorsa che ci interessa usare - Images.jar. Quindi utilizziamo il JarResources'getResource()metodo per fornire i dati grezzi dal file logo.gif per il createImage()metodo del toolkit AWT .

Una nota sulla denominazione

JarResource è un esempio ragionevolmente semplice di come utilizzare i vari servizi forniti da Java 1.1 per manipolare i file di archivio JAR e zip.

Una breve nota sulla denominazione. Il supporto per l'archiviazione in Java in realtà è iniziato utilizzando il popolare formato di archiviazione zip (controlla "Suggerimento Java 21: usa i file di archivio per accelerare il caricamento dell'applet"). Quindi, originariamente, nell'implementare il supporto Java per manipolare i file di archivio, tutte le classi e quant'altro sono state inserite nel pacchetto java.util.zip; queste classi tendono a iniziare con " Zip." Ma da qualche parte nel passaggio a Java 1.1, i poteri che hanno cambiato il nome dell'archivio per essere più focalizzati su Java. Quindi, quelli che ora chiamiamo file JAR sono fondamentalmente file zip.

Come funziona

I campi dati importanti per la JarResourcesclasse vengono utilizzati per tenere traccia e memorizzare il contenuto del file JAR specificato:

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

Quindi, la creazione di istanze della classe imposta il nome del file JAR e quindi richiama il init()metodo per eseguire tutto il lavoro reale:

pubblico JarResources (String jarFileName) {this.jarFileName = jarFileName; dentro(); }

Ora, il init()metodo praticamente carica solo l'intero contenuto del file JAR specificato in una tabella hash (accessibile tramite il nome della risorsa).

Questo è un metodo abbastanza pesante, quindi analizziamolo un po 'ulteriormente. La ZipFileclasse ci fornisce l'accesso di base alle informazioni dell'intestazione dell'archivio JAR / zip. Questo è simile alle informazioni sulla directory in un file system. Qui enumeriamo tutte le voci in ZipFilee costruiamo la tabella hash htSizes con la dimensione di ciascuna risorsa nell'archivio:

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

Successivamente, accediamo all'archivio tramite l'uso della ZipInputStreamclasse. La ZipInputStreamclasse fa tutta la magia per permetterci di leggere ciascuna delle singole risorse nell'archivio. Leggiamo il numero esatto di byte dall'archivio che comprende ciascuna risorsa e memorizziamo tali dati nella tabella hash htJarContents accessibile dal nome della risorsa:

FileInputStream fis = nuovo FileInputStream (jarFileName); BufferedInputStream bis = nuovo BufferedInputStream (fis); ZipInputStream zis = nuovo 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 significa dimensione sconosciuta. if (size == - 1) {size = ((Integer) htSizes.get (ze.getName ())). intValue (); } byte [] b = nuovo byte [(int) size]; int rb = 0; int chunk = 0; while (((int) size - rb)> 0) {chunk = zis.read (b, rb, (int) size - rb); if (chunk == - 1) {break; } rb + = chunk; } // aggiungi alla risorsa interna hashtable 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 (); }}

Si noti che il nome utilizzato per identificare ciascuna risorsa è il nome del percorso qualificato della risorsa nell'archivio, non , ad esempio, il nome di una classe in un pacchetto, ovvero la ZipEntryclasse dal pacchetto java.util.zip essere denominato "java / util / zip / ZipEntry" anziché "java.util.zip.ZipEntry".

L'ultima parte importante del codice è il semplice test driver. Il test driver è una semplice applicazione che accetta un nome di archivio JAR / zip e il nome di una risorsa. Prova a trovare la risorsa nell'archivio e ne segnala il successo o il fallimento:

public static void main (String [] args) genera IOException {if (args.length! = 2) {System.err.println ("usage: java JarResources"); System.exit (1); } JarResources jr = nuovo JarResources (args [0]); byte [] buff = jr.getResource (args [1]); if (buff == null) {System.out.println ("Impossibile trovare" + args [1] + "."); } else {System.out.println ("Found" + args [1] + "(length =" + buff.length + ")."); }}} // Fine della classe JarResources.

E il gioco è fatto. Una classe semplice da usare che nasconde tutta la confusione legata all'uso delle risorse nascoste nei file JAR.

Esercizi per il lettore

Ora che hai la sensazione di estrarre risorse da un file di archivio, ecco alcune indicazioni che potresti voler esplorare per modificare ed estendere la JarResourcesclasse:

  • Invece di caricare tutto durante la costruzione, eseguire il caricamento ritardato. Nel caso di un file JAR di grandi dimensioni, potrebbe non esserci memoria sufficiente per caricare tutti i file durante la costruzione.
  • Invece di fornire semplicemente un metodo di accesso generico come getResource(), potremmo fornire altri strumenti di accesso specifici per la risorsa, ad esempio, getImage()che restituisce un Imageoggetto Java getClass(), che restituisce un Classoggetto Java (con l'aiuto di un caricatore di classi personalizzato) e così via. Se il file JAR è abbastanza piccolo, potremmo precostruire tutte le risorse in base alle loro estensioni (.gif, .class e così via).
  • Alcuni metodi dovrebbero fornire informazioni sul file JAR stesso (fondamentalmente un wrapper ZipFile), tra cui: il numero di voci Jar / zip; un enumeratore che restituisce tutti i nomi delle risorse; funzioni di accesso che restituiscono la lunghezza (e altri attributi) di una particolare voce; e una funzione di accesso che consente l'indicizzazione, per citarne alcuni.
  • JarResourcespuò essere esteso per essere utilizzato dagli applet. Utilizzando i parametri dell'applet e la URLConnectionclasse, il contenuto JAR può essere scaricato dalla rete invece di aprire gli archivi come file locali. Inoltre, possiamo estendere questa classe come gestore di contenuti Java personalizzato.

Conclusione

Se non vedevi l'ora di sapere come estrarre un'immagine da un file JAR, ora hai un modo. Non solo puoi gestire le immagini con un file JAR, ma con la nuova classe fornita in questo suggerimento, lavori la tua magia di estrazione su qualsiasi risorsa da un JAR.

Arthur Choi attualmente lavora per IBM come programmatore di consulenza. Ha lavorato per diverse società, tra cui SamSung Network Laboratory e MITRE. I vari progetti su cui ha lavorato sono sistemi client / server, elaborazione a oggetti distribuiti e gestione della rete. Ha utilizzato una serie di lingue in vari ambienti del sistema operativo. Ha iniziato a programmare nel 1981 con FORTRAN IV e COBOL. Successivamente è passato a C e C ++ e lavora con Java da circa due anni. È particolarmente interessato alle applicazioni di Java nelle aree dei repository di dati attraverso reti geografiche e l'elaborazione parallela e distribuita tramite Internet (utilizzando la programmazione basata su agenti). John Mitchell, dipendente, consulente e preside della propria azienda, ha investito negli ultimi dieci anni nello sviluppo di software per computer all'avanguardia,e nella consulenza e formazione di altri sviluppatori. Ha fornito consulenza su tecnologia Java, compilatori, interpreti, applicazioni basate sul Web e commercio su Internet. John è coautore di Making Sense of Java: A Guide for Managers and the Rest of Us e ha pubblicato articoli su riviste di programmazione. Oltre a scrivere la colonna Java Tips per JavaWorld, modera i newsgroup comp.lang.tcl.announce e comp.binaries.geos.

Ulteriori informazioni su questo argomento

  • Ecco il file di classe JarResources.java//www.javaworld.com/javatips/javatip49/JarResources.java
  • JAR //www.javasoft.com/products/jdk/1.1/docs/guide/jar/index.html
  • Per ulteriori informazioni sul supporto dell'archiviazione in Java, vedere "Java Tip 21Utilizzare i file di archivio per velocizzare il caricamento delle applet" //www.javaworld.com/javatips/jw-javatip21.html

Questa storia, "Java Tip 49: Come estrarre risorse Java da JAR e archivi zip" è stata originariamente pubblicata da JavaWorld.