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.
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");
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 !
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();
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 |
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
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,
...)
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 !