La classe ZIP

 

Il est possible de produire des fichiers compressés au format Zip ou GZip. Cela permettra de gagner de la place et/ou de distribuer qu'un seul fichier compressé par exemple comprenant un ensemble de fichiers/répertoires.

Image non trouvée !Cette classe ne permet pas de gérer les zip avec mot de passe ! Si vous avez ce genre de fichiers à traiter, il faudra utiliser une autre librairie (Par exemple zip4j).

 

Zipper un fichier

La classe Zip s'utilise comme un filtre de flux de données, qui s'ajoute à ceux déjà connus. C'est comme les legos, il suffit de les emboiter !

Zipper un fichier

Alors jouons aux légos:

  • Création d’un flux de données de type binaire en écriture vers un fichier qui sera le fichier compressé:
    FileOutputStream dest = new FileOutputStream ("fichier.zip");

Image non trouvée !Il sera évidemment possible de passer une instance File au lieu du nom de fichier.

  • Création d’un filtre de flux de type buffer de sortie sur le flux de données. Au lieu de travailler caractère par caractère, utilisation d'un buffer.
    BufferedOutputStream buffer = new BufferedOutputStream (dest);
  • Création d’un filtre de flux de type écriture Zip sur le filtre buffer précédemment créé

Maintenant que toute la connectique est faite pour compresser les données et les enregistrer dans un fichier, il reste à lancer la compression effective des données:

Mais avant, il sera possible de préciser la méthode de compression, ainsi que le taux souhaité:

 

Méthode de compression

Il sera possible de préciser la méthode de compression en utilisant la méthode setMethod:

Deux valeurs sont possibles: STORED pour stocker un fichier sans le compresser, DEFLATED pour compresser un fichier (valeur par défaut).

outZip.setMethod (ZipOutputStream.DEFLATED);

 

Taux de compression

Il s'agit d'indiquer le taux de compression. Plus le taux est élevé, plus le fichier a des chances d'être fortement compressé, mais plus il faudra de temps au système pour faire cette compression.

La méthode à utiliser sera setLevel () suivi d'une valeur de 0 (pas de compression) à 9 (meilleur compression). Evidemment, il existe des constantes qu'il est préférable d'utiliser:

Ces constantes sont définies dans une classe Deflater:

BEST_COMPRESSION (La meilleur compression), BEST_SPEED (Compression rapide), DEFAULT_COMPRESSION (Niveau de compression choisi par défaut par le système), DEFAULT_STRATEGY, DEFLATED, FILTERED, HUFFMAN_ONLY (Il s'agit d'une méthode compression codant sur un minimum de bits les valeurs les plus utilisées, et sur un maximum de bits les valeurs rarement utilisées), NO_COMPRESSION (Pas de compression !).

La valeur par défaut est DEFAULT_COMPRESSION

Exemple:

outZip.setLevel(Deflater.BEST_COMPRESSION);

 

Phase de compression

Il est possible de stocker plusieurs fichiers dans un zip. Chacun de ces fichiers représente une entrée dans le fichier zip.

Il faudra donc gérer ces entrées. Evidemment, une classe est prévue à cet effet: ZipEntry()

ZipEntry zipEntry = new ZipEntry(nomEntree);

Généralement, le nomEntree sera tout simplement le nom du fichier à compresser.

outZip.putNextEntry(zipEntry);

puis écriture des données dans le zip

int count;
while((count = bufferInput.read(data, 0, BUFFER)) != -1) {

outZip.write(data, 0, count);

}

Un peu hors sujet, la phase de lecture des fichiers à compresser:

Avec bufferInput qui est un filtre de flux sur le fichier à zipper:

static final int BUFFER = 2048;

La valeur BUFFER à 2048 sera à rapprocher de la taille d'un cluster sur disque pour être optimal (1 cluster rempli = 1 seul accès disque, donc un seul déplacement des têtes, ...). Cette valeur pourra être différente suivant les systèmes et la taille des diques mais doit rester une valeur de puissance de 2 (1024, 2048, 4096, ...).

byte data[] = new byte[BUFFER];

FileInputStream source= new FileInputStream("Nom_Du_Fichier_A_Zipper");
BufferedInputStream bufferInput = new BufferedInputStream(source, BUFFER);

Ici, nous ne traitons qu'un fichier, mais dans votre cas, il faudra certainement en traiter plusieurs !

Image non trouvée !Dans le cas très probable où les noms des fichiers seraient à déterminer par un automatisme, ne pas oublier d'exclure le fichier zip de la liste des noms des fichiers à compresser si celui-ci était généré dans le même répertoire que les fichiers à compresser !

 

Fermeture du flux

Vous avez ouvert pas mal de choses. Il faut donc les refermer:

Fermeture de l’entrée en cours
outZip.closeEntry();

Fermeture du flux sur le fichier en entrée
bufferInput.close();

source.close();

Si vous avez terminé de générer votre fichier Zip

Fermeture des flux

outZip.close();

buffer.close();
dest.close();

Image non trouvée !Une fois le flux fermé, il ne sera plus possible d'ajouter ou de supprimer le contenu du fichier ZIP. La seule solution possible sera d'extraire tous les fichiers zippés et de reconstruire un nouveau fichier zip.

 

Exemple de code pour zipper un fichier

Création d'un fichier nommé fichier.zip sous c:\. Ce fichier va contenir une entrée nommée coucou.txt contenant le texte "Coucou".

Il est tout à fait possible de compresser le contenu d'un fichier...

 

Lister les entrées d'un zip

Maintenant que nous avons un fichier Zip, il serait intéressant de lister les entrées de ce zip !

Il faut tout d'abord ouvrir le fichier Zip:

ZipFile zipFile = new ZipFile("nom_fichier_zip.zip"); ou avec une instance file en paramètre

Récupération d'une énumération des entrées
Enumeration<? extends ZipEntry> = zipFile.entries()

Parcours de chacune des entrées
while(entries.hasMoreElements()) {
ZipEntry entree = entries.nextElement();

...

}

 

Propriétés des entrées

Pour une entrée dans un zip, différentes informations peuvent être récupérées, d'où l'utilisations de getters:

Propriété Description
String getComment Permet de récupérer un éventuel commentaire sur l'entrée (null si absent)
long getCompressedSize() Retourne la taille compressée de l'entrée, ou -1 si inconnue
long getCrc() Retourne la valeur calculée du CRC ou -1 si inconnue
byte[] getExtra() Retourne le champ extra d'une entrée ou null si absent
int getMethod() Méthode de compression utilisée ou -1 si inconnue
String getName() Retourne le nom de l'entrée
long getSize() Retourne la taille de l'entrée avant compression, ou -1 si inconnue
long getTime() Retourne la date et l'heure de dernière modification ou -1 si inconnue (format TimeStamp)
int hashCode() Hashcode de l'entrée
boolean isDirectory() Indique si vrai que l'entrée est un répertoire
toString Représentation d'une entrée (classique comme méthode !)

Qui dit getter dit setter !

Méthode Désignation
void setComment(String comment) Pour positionner un commentaire
void setCompressedSize(long csize) Taille de l'entrée compressée
void setCrc(long crc) CRC 32 de l'entrée non compressée
void setExtra(byte[] extra) Positionner le champ de donnée extra
void setMethod(int method) Méthode de compression
void setSize(long size) Taille de l'entrée non compressée
void setTime(long time) Date et heure de la dernière modification de l'entrée

Image non trouvée !Vous n'avez pas forcement besoin de faire appel à ces setters, le système renseignant les propriétés automatiquement.

 

Exemple de code pour traiter les entrées d'un ZIP

 

Dézipper un/des fichiers

Il s'agit de faire la même chose que lors de la création d'un fichier ZIP mais en lecture !

Donc toujours les legos pour imbriquer les différents filtres de flux: Zip, buffer et une valeur de taille de tampon pour les buffers d’entrée et de sortie utilisée par la suite.

Ouverture du fichier à décompresser, utilisation d'un flux de données en lecture de type FileInputStream
FileInputStream inZip= new FileInputStream("fichier_zip_a_lire.zip");

Là encore, pour une question de performance, utilisation des buffers (filtre de flux de type buffer):

static final int BUFFER = 2048;
byte data[] = new byte[BUFFER];

BufferedInputStream bufferInput = new BufferedInputStream(inZip);

Puis utilisation d'un filtre de flux de type ZIP pour pouvoir interpréter les données du zip.
ZipInputStream zipInput = new ZipInputStream(bufferInput);

Vous savez maintenant qu'un fichier zip est composé d'entrée. Il ne reste plus qu'à accéder à ces entrées:

ZipEntry entry;
while((entry = zipInput.getNextEntry()) != null) {


Création du fichier de sortie à partir du nom de l’entrée
FileOutputStream fileOutput = new FileOutputStream(entry.getName());

Là encore, utilisation d'un buffer sur le fichier en sortie
bufferOutput = new BufferedOutputStream(fileOutput, BUFFER);

Il ne reste plus qu'à écrire les données dézippées dans le fichier de sortie
while ((taille = zipInput.read(data, 0, BUFFER)) != -1) {


bufferOutput.write(data, 0, taille);

}

Vidage du cache en écriture
bufferOutput.flush();

Fermeture du fichier en sortie
bufferOutput.close();

fileOutput.close();


}

Si nous en avons fini avec la décompression (c'est le cas dans cet exemple), fermeture de l’archive:

zipInput.close(); // le filtre Zip

bufferInput.close(); // Le filtre buffer

inZip.close(); // Et le flux

Image non trouvée !Le code n'est pas complet, il y a des choses à rajouter comme le traitement des répertoires (tester isDirectory() de l'entrée zip, création, ...)

Image non trouvée !Avec ce code, le/les fichiers seront extraits dans le répertoire courant. En principe dans le répertoire de votre projet.

 

Exemple de code de dézippage

On reprend le fichier zip créé précédemment.

 

Zip4J

Une autre classe non standard permet de traiter les Zip avec mot de passe. Pour plus d'explications, voyez ici !