I. Introduction

Un programme informatique manipule souvent des fichiers dont certains doivent être compressés du fait de leur volume ou par souci de créer une archive unique facilement distribuable.

Le format Zip est largement répandu, son algorithme est simple et libre.

II. Présentation de l'API Zip

L'API Zip de Java est présente dans le JDK depuis la version 1.1.

Elle permet de manipuler des fichiers au format Zip et GZip. L'algorithme utilisé est la méthode DEFLATE. Cette API fournit également des méthodes utilitaires pour le contrôle de l'intégrité des fichiers via les méthodes CRC-32 et Adler-32.

Cette API s'appelle de cette façon :

 
Sélectionnez
import java.util.zip.*;

III. Compression

La compression nécessite de connaître :

  • le ou les fichiers à compresser ;
  • le nom de l'archive à créer ;
  • la méthode de compression (optionnelle) ;
  • le taux de compression (optionnelle).

III-A. Fichier de sortie

  • Une valeur de taille de tampon pour les buffers d'entrée et de sortie utilisés par la suite, et un buffer de données :
 
Sélectionnez
static final int BUFFER = 2048;
byte data[] = new byte[BUFFER];
  • Création d'un flux d'écriture vers un fichier, ce fichier sera l'archive Zip finale :
 
Sélectionnez
FileOutputStream dest= new FileOutputStream("archive.zip");
  • Création d'un buffer de sortie afin d'améliorer les performances d'écriture :
 
Sélectionnez
BufferedOutputStream buff = new BufferedOutputStream(dest);
  • Création d'un flux d'écriture Zip vers ce fichier à travers le buffer :
 
Sélectionnez
ZipOutputStream out = new ZipOutputStream(buff);

III-B. Paramètres

  • Spécifier la méthode de compression désirée :
 
Sélectionnez
out.setMethod(ZipOutputStream.DEFLATED);
  • Spécifier le taux de compression (entier positif entre 0 et 9) :
 
Sélectionnez
out.setLevel(9);

III-C. Entrées de l'archive

  • Lister les fichiers à compresser. Dans notre exemple, ils sont supposés être dans le tableau files[] :
 
Sélectionnez
for(int i=0; i<files.length; i++) {
    FileInputStream fi = new FileInputStream(files[i]);
    …
  • Leur créer un buffer d'entrée :
 
Sélectionnez
    BufferedInputStream buffi = new BufferedInputStream(fi, BUFFER);
  • Pour chacun d'eux, créer une entrée Zip :
 
Sélectionnez
    ZipEntry entry= new ZipEntry(files[i]));
  • Et affecter cette entrée au flux de sortie :
 
Sélectionnez
    out.putNextEntry(entry);

III-D. Écriture de l'archive

  • Écriture des entrées dans le flux de sortie par paquets de taille égale aux tampons d'entrée et de sortie :
 
Sélectionnez
    int count;
    while((count = buffi.read(data, 0, BUFFER)) != -1) {
        out.write(data, 0, count);
    }
  • Fermeture de l'entrée en cours :
 
Sélectionnez
    out.closeEntry();
  • Fermeture des flux :
 
Sélectionnez
    buffi.close();
}
out.close();

Et voilà, votre archive Zip est créée. Elle pourra être décompressée avec WinZip (Windows) ou unzip (Unix).

IV. Listage des entrées d'une archive

Pour lister le contenu d'une archive Zip et en afficher les propriétés il faut procéder ainsi :

  • ouverture du fichier Zip :
 
Sélectionnez
ZipFile zf = new ZipFile("archive.zip");
  • extraction de ses entrées :
 
Sélectionnez
Enumeration entries = zf.entries()
  • parcours de chacune des entrées :
 
Sélectionnez
while(entries.hasMoreElements()) {
    ZipEntrye = (ZipEntry)entries.nextElement();
    …
  • extraction des informations désirées :
 
Sélectionnez
    System.out.println(e.getName());
}

Et voilà !

V. Propriétés d'une entrée

Méthode Description
String getComment() Retourne le commentaire associé à l'entrée, null si aucun
long getCompressedSize() Retourne la taille de l'entrée après compression, -1 si inconnue
long getCrc() Retourne le contrôle d'erreur cyclique CRC ou -1 si inconnu
byte[] getExtra() Retourne le champ optionnel ou -1 si inconnu
int getMethod() Retourne la méthode de compression employée, -1 si inconnue
String getName() Retourne le nom de l'entrée
long getSize() Retourne la taille de l'entrée avant compression, -1 si inconnue
long getTime() Retourne la date et heure de dernière modification, -1 si inconnue
int hashCode() Retourne le hashcode (condensat) de cette entrée
boolean isDirectory() Retourne vrai (true) si l'entrée est un répertoire
String toString() Retourne une représentation de l'entrée sous forme de chaîne de caractères

Les getters précédents ont leurs setters associés ci-bas :

Méthode Description
void setComment(String c) Affectation du commentaire associé à l'entrée
void setCompressedSize(long csize) Affectation de la taille de l'entrée après compression
void setCrc(long crc) Affectation du CRC de l'entrée
void setExtra(byte[] extra) Affectation d'un champ optionnel
void setMethod(int method) Affectation d'une méthode de compression à l'entrée
void setSize(long size) Affectation de la taille de l'entrée avant compression
void setTime(long time) Affectation de la date et heure de dernière modification

VI. Ratio

Le calcul du ratio s'effectue ainsi :

Image non disponible

Exemple :

 
Sélectionnez
System.out.println(e.getName() + " ratio: " + ((e.getSize() - e.getCompressedSize()) * 100) / e.getSize() + "%");

VII. Date

La date est au format TimeStamp (nombre de secondes écoulées entre la date et le 1er janvier 1970), il est alors nécessaire de la formater au format voulu (ici, francophone).

Exemple :

 
Sélectionnez
DateFormat df = new SimpleDateFormat("dd/mm/yyyy hh:mm:ss");
Dated = null;
d = new Date(e.getTime());
System.out.println(" date: " + df.format(d));

VIII. Méthode

Il n'existe que deux méthodes : DEFLATED (avec compression) et STORED (sans compression).

Exemple :

 
Sélectionnez
String method = null;
if (e.getMethod() == ZipEntry.DEFLATED) {
    method = new String("DEFLATED");
} else if(e.getMethod() == ZipEntry.STORED) {
    method = new String("STORED");
}

IX. Qualité de la compression

Il existe 10 niveaux, numérotés de 0 à 9.

Il existe aussi des constantes :

Constante Description Niveau
Deflater.BEST_COMPRESSION Meilleure compression 9
Deflater.DEFAULT_COMPRESSION Compression par défaut 5
Deflater.BEST_SPEED Meilleure rapidité 1
Deflater.NO_COMPRESSION Pas de compression 0

Exemple :

 
Sélectionnez
out.setLevel(Deflater.BEST_COMPRESSION);

X. Décompression

X-A. Fichiers d'entrée

  • Une valeur de taille de tampon pour les buffers d'entrée et de sortie utilisés par la suite, et un buffer de données :
 
Sélectionnez
static final int BUFFER = 2048;
byte data[] = new byte[BUFFER];
  • Déclaration d'un fichier destination :
 
Sélectionnez
BufferedOutputStream dest = null;
  • Ouverture du fichier à décompresser :
 
Sélectionnez
FileInputStream fis = new FileInputStream("archive.zip");
  • Ouverture buffer sur ce fichier :
 
Sélectionnez
BufferedInputStream buffi = new BufferedInputStream(fis);
  • Ouverture de l'archive Zip via ce buffer :
 
Sélectionnez
ZipInputStream zis = new ZipInputStream(buffi);

X-B. Écriture

  • Parcours des entrées de l'archive :
 
Sélectionnez
ZipEntry entry;
while((entry = zis.getNextEntry()) != null) {
  • Création du fichier de sortie à partir du nom de l'entrée :
 
Sélectionnez
    FileOutputStream fos = new FileOutputStream(entry.getName());
  • Affectation au buffer de sortie de ce flux vers fichier :
 
Sélectionnez
    dest = new BufferedOutputStream(fos, BUFFER);
  • Écriture sur disque :
 
Sélectionnez
    while ((count = zis.read(data, 0, BUFFER)) != -1) {
        dest.write(data, 0, count);
    }

X-C. Fermeture

  • Vidage du tampon en écriture :
 
Sélectionnez
    dest.flush();
  • Fermeture du flux de sortie :
 
Sélectionnez
    dest.close();
}
  • Fermeture de l'archive :
 
Sélectionnez
zis.close();

XI. Checksums

Pour inclure un contrôle d'intégrité dans vos applications, il faut ajouter un flux de contrôle entre le flux d'écriture sur fichier et le flux du buffer de sortie. Il existe deux méthodes de contrôle : Adler32 et CRC32. La première est la plus rapide.

Exemple :

 
Sélectionnez
// création d'un flux d'écriture sur fichier
FileOutputStream dest= new FileOutputStream("archive.zip");
// ajout du checksum : Adler32 (plus rapide) ou CRC32
CheckedOutputStream checksum = new CheckedOutputStream(dest, new Adler32());
// création d'un buffer d'écriture
BufferedOutputStream buff = newBufferedOutputStream(checksum);
// création d'un flux d'écriture Zip
ZipOutputStream out = new ZipOutputStream(buff);

XII. Accents

Malheureusement, les accents et caractères spéciaux sont mal interprétés par l'API java.util.zip, il faut donc transformer les noms qui en contiennent.

Exemple d'une fonction effectuant la conversion :

 
Sélectionnez
import sun.text.Normalizer;
public static String unAccent(String s) {
    String temp = Normalizer.normalize(s, Normalizer.DECOMP, 0);
    return temp.replaceAll("[^\\p{ASCII}]","");
}

Que l'on ajoute ici :

 
Sélectionnez
ZipEntry entry = new ZipEntry(unAccent(files[i]));

XIII. Méthode rapide : ZipFile

Il existe un moyen d'accéder rapidement aux entrées d'une archive :

 
Sélectionnez
ZipFile zipfile = newZipFile("archive.zip");
Enumeration entries = zipfile.entries();
while (entries.hasMoreElements()) {
    ZipEntry e = ((ZipEntry)entries.nextElement());
    System.out.println(e.getName());
}
zipfile.close();

Voici la liste des méthodes de la classe ZipFile :

Méthode Description
ZipFile(String name) Ouvre l'archive Zip
void close() Ferme l'archive Zip
Enumeration entries() Retourne l'ensemble des entrées
void finalize() Fermeture de l'archive, lorsqu'inactive
ZipEntry getEntry(String name) Retourne l'entrée de nom spécifié
InputStream getInputStream(ZipEntry entry) Retourne un flux d'entrée vers le fichier associé à l'entrée spécifiée
String getName() Retourne le chemin de l'archive
int size() Retourne le nombre d'entrées

XIV. Historique

20 juin 2004 : création du document (24 diapos).

Agissez sur la qualité de ce document en envoyant vos critiques et suggestions à l'auteur.

Pour toute question technique, se reporter au forum Java de Developpez.com.

Reproduction autorisée uniquement pour un usage non commercial.

XV. Note et remerciement du gabarisateur

Cet article a été mis au gabarit de developpez.com. Voici le lien vers le PDF d'origine : zip.pdf.

Le gabarisateur remercie Claude LELOUP pour sa correction orthographique.