|
Michele Sciabarrà
Il JDK supporta il formato JAR di archiviazione dei
programmi in Java, che sono in realtà in formato ZIP. Niente
di strano quindi che internamente sia presente il supporto per i file
ZIP. Saggiamente quindi JavaSoft ha deciso di rendere disponibili
delle classi per l'uso generale del formato zip. Da notare che gli
zip non sono interamente gestiti in Java. Infatti tra le DLL del JRE
(Java Runtime Environment) esiste una zip.dll che chiaramente
implementa in C le chiamate native necessarie per la gestione degli
ZIP. Si tratta, con tutta probabilità, di codice derivato
dalla versione freeware dello zip nota come Info-Zip. In realtà
il supporto al formato ZIP non è completo. Lo ZIP prevede
diversi metodi di compressione, mentre è previsto il solo
supporto per la compressione deflating, che ha 9 livelli diversi di
compressione, e per lo storing senza compressione. Altresì è
previsto il supporto per la pura compressione deflating senza
archiviazione ZIP, il formato utilizzato dalla famora utility gzip,
che come è noto non è un archiviatore ma solo un
compressore. Si tratta comunque di tutto e solo quello che è
necessario in pratica: le altre forme di compressione dello ZIP sono
un residuo storico del PKZIP 1.0, mentre il deflating è la
compressione introdotta nel PKZIP 2.0 che è la più
efficiente in pratica. I livelli di compressione, più alti
sono più è efficiente la compressione, ma è
anche più lenta. In questo articolo metteremo alla prova la
gestione degli zip e ne impararemo l'uso, implementando una piccola
utility a linea di comando, jzip, che permette di listare,
scompattare e creare degli zip. La sintassi dell'utility (che ricorda
quella del comando tar ) è:
java jzip.JZip -t <filename.zip>
show zip contents
java jzip.JZip -x <filename.zip>
extract files
java jzip.JZip -c <filename.zip> <files>...
create a zip with files
Tralasciamo di discutere i dettagli (piuttosto ovvi) relativi alla
gestione della riga di comando e concentriamoci nell'analisi dei tre
metodi principali della classe jzip.Jzip che sono:
void list(String filename)
void unzip(String filename)
void zip(String filename, String[] files)
Listare uno zip
In questo paragrafo faremo riferimento a JZip.list. Per accedere ad
un file zip si utilizza la classe ZipFile; uno zip è un
archivio, e contiene al suo interno altri file. Ogni file contenuto è
rappresentato dalla classe ZipEntry. Un oggetto ZipFile può
essere costruito specificando un file per nome (una stringa) o un
oggetto di tipo File. In JZip.list uno ZipFile viene costruito usando
come argomento il nome del file specificato sulla riga di comando.
Tramite ZipFile.entries(), ottenieamo una enumerazione delle ZipEntry
contenute nel file. La classe ZipEntry fornisce i metodi per estrarre
le informazioni sui file contenuti nello zip. Quindi per listare i
contenuti la nostra utility deve enumerare le entry; per ciascuna
deve leggere le informazioni e stampare il risultato. Ci sono alcuni
dettagli a cui dobbiamo stare attenti.
Notiamo innanzitutto l'uso del SimpleDateFormat per stampare la
data. Infatti ZipEntry.getSize() fornisce la data in secondi dal
primo gennaio 1970, che deve essere convertito in una stringa con
l'uso dell'apposito formattatore di date. Un altro trucco è
quello utilizzato per allineare a destra un numero, cioè la
lunghezza del file in byte: abbiamo aggiunto degli spazi in testa ed
estratto la sottostringa di coda. Notiamo infine come, listando i
file contenuti in uno zip, i file hanno sempre il pathname separato
da slash diritti anche se sono stati originariamente creati in
ambiente Windows. Questa caratteristica degli ha delle conseguenza a
cui dobbiamo fare attenzione quando tentiamo di scompattare.
Scompattare uno zip
La procedura di scompattamento assomiglia al listing nella parte
relativa all'estrazione delle entry. Per ciascuna entry dobbiamo però
dearchiviare il file zip anzichè esaminare e stampare la
entry. Un fle viene estratto utilizzando un output stream, fornito
dalla classe ZipFile specificando la entry. Cioè se zip è
uno ZipFile e entry è una ZipEntry corrispondente al file che
vogliamo estrarre, otteniamo l'input stream in con:
InputStream in = zip.getInputStream(entry);
Una volta ottenuto l'input stream, possiamo leggerlo e scriverlo in
un file.
Per creare e scrivere un file possiamo utilizzare un
FileOutputStream: in teoria basterebbe
OutputStream out =
new FileOutputStream(entry.getName())
però questo codice non funziona per due motivi. Il primo è
che gli slash sono sempre diritti, mentre sotto Windows devono essere
inversi. Il secondo è che la directory in cui si vuole
scrivere il file deve già esistere, cosa che in generale non è
vera. Per ovviare a questi problemi, per prima cosa "naturalizziamo"
il nome del file, trasformando gli slash nel
ZipEntry entry = (ZipEntry)e.nextElement();
StringBuffer fixed = new StringBuffer(entry.getName());
for( int i = 0; i<fixed.length(); ++i)
if( fixed.charAt(i) == '/')
fixed.setCharAt(i, File.separatorChar);
Fatto questo creiamo la directory dove si trova il file sfruttando File.mkdirs, e ovviamente non creando alcun file se l'entry è relativa ad una directory.
File file = new File(fixed.toString());
if(entry.isDirectory()) {
file.mkdirs();
continue; // non bisogna creare file
} else {
String dir = file.getParent();
if( dir != null)
new File(dir).mkdirs();
}
A questo punto non ci resta che estrarre il file:
System.out.println("unzipping: "+file);
OutputStream out = new FileOutputStream(file);
InputStream in = zip.getInputStream(entry);
while( (n = in.read(buff, 0, buff.length))!= -1)
out.write(buff, 0 , n);
}
}
|