CERCA SITEMAP 1280
Ultimo aggiornamento: 30 Agosto 2009

Compressione Zip

Molte sorgenti di informazioni contengono dati ridondanti che appesantiscono le comunicazioni fra Client e Server ed in generale fra diversi computer.
In questo senso la compressione svolge una funzione molto importante perché a fronte di un piccolo costo di tempo (necessario a comprimere i dati all’origine e a decomprimerli alla sorgente) permette di eliminare tali ridondanze risparmiando risorse di memoria e/o di banda.

Generalmente l’approccio alla compressione di dati può avvenire:
  • via software mediante l’applicazione di determinati algoritmi (si pensi ad esempio alla codifica di Huffman)
  • via hardware mediante un circuito dedicato allo svolgimenti di tali operazioni
Compressione e decompressione
La operazioni di compressione e decompressione sono l’una l’inverso dell’altra e pertanto sono rappresentabili mediante una black-box bidirezionale.
Le tecniche di compressione si dividono generalmente in due categorie:
  • lossy che comportano una perdita di dati e quindi una diminuzione della qualità delle informazioni (si pensi ad esempio al formato jpg per le immagini)
  • lossless che comprimono i dati senza alcuna perdita e quindi il flusso di informazioni può essere completamente ricostruito
Alcuni fra i formati di compressione dati più diffusi sono:
  • zip che supporta diversi algoritmi di compressione e sebbene altri formati offrano rapporti di compressione maggiori la sua diffusione gli permette di essere uno dei formati per compressione dati più diffusi
  • gzip GNU Zip che fu inizialmente creato da Jean-loup Gailly and Mark Adler ed usa l’algoritmo di Lempel-Ziv
In java il package java.util.zip contiene classi per la modifica, la creazione e la lettura di file in formato zip e gzip nonché classi per il calcolo di checksum di input strema arbitrari che possono essere utilizzati per validare i dati.

Decompressione dei dati

Per decomprimere un file zip è possibile agire in due distinti modi:
  • accedendo sequenzialmente a tutti i file compressi all’interno dell’archivio zip
  • accedendo in maniera casuale ai file compressi all’interno dell’archivio zip

Accesso sequenziale ai file

L’accesso sequenziale ai file compressi all’interno di un archivio zip e la loro conseguente estrazione si avvale della classe ZipInputStream.
Tale classe infatti mediante il metodo getNextEntry() permette di recuperare i punti di accesso dei vari file memorizzati all’interno dell’archivio sottoforma di ZipEntry.

Un oggetto ZipEntry memorizza informazioni relative:
  • al nome del file compresso getName()
  • alla dimensione del file compresso getCompressedSize()
  • al commento associato getComment()
  • al metodo di compressione utilizzato getMethod()
  • alla dimensione del file non compresso getSize()
Per estrarre i file occorre quindi:
  • creare uno ZipInputStream associato al file zip da decomprimere
    FileInputStream fis = new FileInputStream("filezip.zip");
    ZipInputStream zin = new ZipInputStream(new BufferedInputStream(fis));
    
  • identificare i punti di accesso ZipEntry dei vari file memorizzati all’interno ed avviarne l’estrazione
    ZipEntry entry;
    
    while((entry = zin.getNextEntry()) != null)
    {
       ...
    }
    
Ecco un esempio completo:
try
{
   BufferedOutputStream output = null;
   FileInputStream fis = new FileInputStream("filezip.zip");
   ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fis));

   ZipEntry entry;

   while((entry = zis.getNextEntry()) != null)
   {
      int count;
      byte dati[] = new byte[1024];
      FileOutputStream fos = new FileOutputStream(entry.getName());
      output = new BufferedOutputStream(fos, 1024);

      while ((count = zis.read(dati, 0, 1024))!= -1)
      {
         output.write(dati, 0, count);
      }

      output.flush();
      output.close();
   }

   zis.close();
}
catch(Exception e)
{
   ...
}

Accesso casuale ai file

Per accedere direttamente ai file compressi all’interno di un archivio zip è possibile ricorrere alla classe ZipFile che è appunto rappresentativa di un archivio zip.

La classe ZipFile contiene fra gli altri i metodi:
  • getEntry(String name) che permette di recuperare lo ZipEntry relative ad un determinato file (specificato dal nome passato come parametro)
  • entries() che restituisce sotto forma di un oggetto Enumeration tutti gli ZipEntry dei file memorizzati all’interno dell’archivio zip
Una volta identificato il file da estrarre ed il relativo ZipEntry è possibile creare il relativo InputStream e quindi procedere all’estrazione nel seguente modo:
...

ZipFile zipfile = new ZipFile("filezip.zip");
Enumeration e = zipfile.entries();
ZipEntry entry = (ZipEntry) zipfile.getEntry("miofile");

BufferedInputStream is = new BufferedInputStream(zipfile.getInputStream(entry));

...

Compressione dei dati

Analogamente a quanto visto per la decompressione di archivi, la compressione di file in formato zip si avvale della classe ZipOutputStream che permette di ottenere l’OutputStream del file sul quale vogliamo scrivere i nostri dati compressi.
FileOutputStream output = new FileOutputStream("filezip.zip");
ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(output));
Ottenuto l’oggetto ZipOutputStream occorre aprire un InputStream relativo alla sorgente di dati che vogliamo comprimere:
FileInputStream fi = new FileInputStream(miofile);
Creare uno ZipEntry per il file in questione:
ZipEntry entry = new ZipEntry(miofile));
e registrarlo all’interno del flusso di output sfruttando il metodo putNextEntry(ZipEntry e):
out.putNextEntry(entry);
ed infine procedere al salvataggio dei dati mediante la lettura dalla sorgente di input ed il salvataggio nel file di destinazione.

Ecco un esempio completo:
BufferedInputStream input = null;
FileOutputStream output = new FileOutputStream("filezip.zip");
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(output));

out.setMethod(ZipOutputStream.DEFLATED);

byte data[] = new byte[1024];

File f = new File(miofile);

FileInputStream fi = new FileInputStream(miofile);
input = new BufferedInputStream(fi, 1024);

ZipEntry entry = new ZipEntry(miofile);
out.putNextEntry(entry);

int count;

while((count = input.read(data, 0, 1024)) != -1)
{
   out.write(data, 0, count);
}
input.close();

out.close();

Checksum

Molto spesso nelle comunicazioni Client-Server si verificano degli errori di comunicazione: l’interfaccia Checksum (e le sue implementazioni concrete Adler32 e CRC32) definisce appunto una somma di controllo per identificare eventuali errori di trasmissione o di corruzione di file presenti all’interno di un archivio zip.
Tipicamente la Checksum viene aggiunta al flusso di dati e ricalcolata alla sorgente: se le due checksum sono diverse allora i dati ricevuti sono corrotti altrimenti i dati ricevuti non hanno subito errori.
Per creare uno ZipOutputStream checksum-based è possibile utilizzare la seguente forma:
...

CheckedOutputStream checksum =new CheckedOutputStream(dest, new Adler32());
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(checksum));

...
Analogamente per creare uno ZipInputStream è possibile scrivere:
...

CheckedInputStream checksum = new CheckedInputStream(fis, new Adler32());
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(checksum));

...
Per recuperare la Checksum da un flusso dati è possibile utilizzare il metodo getChecksum() delle classi CheckedOutputStream e CheckedInputStream.