7. Flux compressés

7.1. Introduction

L'API Java I/O propose le support direct pour écrire et lire des fichiers au format GZip et Zip. Ce support est utilisé en interne, puisque les fichiers archive Java JAR peuvent eux-mêmes être compressés au format Zip. Il est également utilisable dans les applications.

7.2. Flux de type gzip

Le format GZip est un format compressé, qui ne supporte l'écriture que d'un unique fichier. À la différence d'un fichier Zip, un fichier GZip ne peut pas contenir de structure de répertoire, ou plusieurs fichiers. L'écriture d'un fichier GZip utilise la classe GZIPOutputStream, et la lecture GZIPInputStream. Ces deux classes étendent indirectement OutputStream et InputStream respectivement, et prennent les mêmes objets en paramètre. Ces classes se trouvent dans le package java.util.zip La création d'un fichier GZip est donc immédiate.

Exemple 132. Utilisation de flux GZip

// dans une méthode main
 // déclaration d'un fichier
File fichier =  new File("tmp/bonjour.txt.gz") ;

 // création d'un flux compressé sur ce fichier
GZIPOutputStream gzos =  new GZIPOutputStream(new FileOutputStream(fichier)) ;

 // création d'un flux data sur ce flux compressé
DataOutputStream dos =  new DataOutputStream(gzos) ;

 // écriture de Bonjour le monde !
dos.writeUTF("Bonjour le monde !") ;

 // fermeture de ce flux, à mettre dans un bloc finally
gzos.close() ;

 // puis ouverture du flux compressé sur ce fichier
GZIPInputStream gzis =  new GZIPInputStream(new FileInputStream(fichier)) ;

 // ouverture d'un flux data sur ce flux compressé
DataInputStream dis =  new DataInputStream(gzis) ;

 // lecture d'une chaîne de caractères
String lue = dis.readUTF() ;

 // fermeture du flux dans un bloc finally
gzis.close() ;

System.out.println(lue) ;

Comme on le voit, l'écriture d'un flux GZip n'est pas plus compliquée que l'écriture d'un flux normal, il s'agit juste d'une déclaration supplémentaire.

7.3. Flux de type zip

L'écriture de fichier Zip est un peu plus complexe, du fait qu'un fichier Zip possède une structure interne, et qu'il faut créer cette structure. On peut donc créer des répertoires, des fichiers, des fichiers dans des répertoires ou non. Chaque élément, répertoire ou fichier, est modélisé par une instance de ZipEntry. Pour créer un tel élément dans le fichier Zip, il faut l'ajouter au flux par appel à sa méthode createEntry(). Le flux de lecture expose une méthode nextEntry(), qui permet de lire les entrées d'un fichier zip les unes à la suite des autres. Voyons ceci sur un exemple.

Exemple 133. Création d'un fichier Zip

// dans une méthode main
 // déclaration d'un fichier
File fichier =  new File("tmp/bonjour.zip") ;

 // ouverture d'un flux Zip sur ce fichier
ZipOutputStream zos =  new ZipOutputStream(new FileOutputStream(fichier)) ;

 // création d'un répertoire : il s'agit d'une entrée dont le nom
 // se termine par un /
ZipEntry entry =  new ZipEntry("vide/") ;
zos.putNextEntry(entry) ;

 // création d'un autre répertoire 
entry =  new ZipEntry("bonjour/") ;
zos.putNextEntry(entry) ;

 // création d'un fichier Bonjour-1.txt dans ce répertoire
entry =  new ZipEntry("bonjour/Bonjour-1.txt") ;
zos.putNextEntry(entry) ;

 // ouverture d'un flux Data sur ce flux Zip
DataOutputStream dos =  new DataOutputStream(zos) ;
 // écriture d'un message dans ce fichier
dos.writeUTF("Bonjour le monde ! [1]") ;

 // création d'un autre fichier
entry =  new ZipEntry("Bonjour-2.txt") ;
zos.putNextEntry(entry) ;

 // écriture d'un message dans ce fichier
dos.writeUTF("Bonjour le monde ! [2]") ;

 // fermeture d'un fichier dans le bloc finally
dos.close() ;

On peut ouvrir le fichier bonjour.zip à l'aide d'un utilitaire classique, tel que Winzip, WinRAR ou 7z. On constate bien que ce fichier comporte deux répertoires internes, et nos deux fichiers, le premier dans le répertoire bonjour, et le second dans le répertoire racine. Écrivons maintenant le code de lecture de ce fichier. Ce code doit commencer par analyser les entrées de ce fichier, et tester à chaque fois si cette entrée est un répertoire ou un fichier. S'il s'agit d'un fichier, alors on peut lire son contenu.

Exemple 134. Lecture d'un fichier Zip

// ouverture d'un flux Zip sur le fichier de l'exemple précédent
ZipInputStream zis =  new ZipInputStream(new FileInputStream(fichier)) ;
 // ouverture d'un flux Data sur ce flux
DataInputStream dis =  new DataInputStream(zis) ;

 // lecture de la première entrèe
 // cette méthode retourne null s'il n'y a plus d'entrée à lire
entry = zis.getNextEntry() ;
 while (entry != null) {

    if (entry.isDirectory()) {
   
       // cette entrée est un répertoire, on affiche son nom
      System.out.println("Répertoire : " + entry) ;
   }  else {
   
       // cette entrée est un fichier, on affiche son contenu
      System.out.println("Fichier : " + entry) ;
      System.out.println(dis.readUTF()) ;
   }

    // on boucle sur toutes les entrées
   entry = zis.getNextEntry() ;
}

L'exécution de cette application donne le résultat suivant :
 Répertoire : vide/
 Répertoire : bonjour/
 Fichier : bonjour/Bonjour-1.txt
 Bonjour le monde ! [1]
 Fichier : Bonjour-2.txt
 Bonjour le monde ! [2]
Java langage & API
Retour au blog Java le soir
Cours & Tutoriaux
Table des matières
Introduction : un peu d'histoire
1. Java : genèse d'un nouveau langage
Programmer en Java
1. Un premier exemple
1.1. Aperçu général, cycle de vie
1.2. Un premier programme
1.3. Programmes, applets, servlets, etc...
2. Une première classe
2.1. Écriture d'une classe
2.2. Instanciation de cette classe
3. Types de base, classes et objets
3.1. Types de base
3.2. Classes et objets
Classes Object et String
1. Introduction
2. La classe Object
2.1. La méthode toString()
2.2. La méthode clone()
2.3. La méthode equals()
2.4. La méthode hashCode()
2.5. La méthode finalize()
2.6. La méthode getClass()
3. La classe String
3.1. Introduction
3.2. Construction d'un objet de type String
3.3. Concaténation, StringBuffer et StringBuilder
3.4. Concaténations de chaînes de caractères depuis Java 8
3.5. Concaténations de chaînes de caractères depuis Java 11
3.6. Extraction d'une sous-chaîne de caractères
3.7. Comparaison de deux chaînes de caractères
3.8. Méthodes de comparaisons lexicographiques
3.9. Méthode de recherche de caractères
3.10. Méthode de modification de chaîne
3.11. Méthode de duplication
3.12. Support de l'unicode, internationalisation
Structure d'une classe
1. Introduction
2. Classes
2.1. Classes publiques
2.2. Classes internes
2.3. Classe membre
2.4. Classes locales
2.5. Classes anonymes
2.6. Le mot-clé this
3. Éléments statiques
3.1. Champ statique
3.2. Cas des constantes
3.3. Bloc statique
3.4. Classe membre statique
4. Membres d'une classe, visibilité
4.1. Bloc non statique
4.2. Accès à un membre, visibilité
4.3. Les champs
4.4. Signature d'une méthode
4.5. Les méthodes
4.6. Getters et Setters
5. Constructeur, instanciation
5.1. Chargement d'une classe
5.2. Constructeurs d'une classe
5.3. Instanciation d'un objet
5.4. Destruction d'objets
5.5. Le mot-clé final
6. Énumérations
6.1. Déclaration d'une énumération
6.2. Classe énumération
6.3. Méthode toString()
6.4. Méthode valueOf()
6.5. Méthode values()
6.6. Méthode ordinal()
6.7. Méthode compareTo()
6.8. Constructeurs privés
6.9. Classe utilitaire : EnumSet
Noms, opérateurs, tableaux
1. Introduction
2. Identificateurs, noms et expressions
2.1. Identificateurs et noms
2.2. Expressions
3. Opérateurs, ordre d'exécution
3.1. Ordre d'exécution
3.2. Les opérateurs
3.3. Les opérateurs ++ et --
3.4. Les opérateurs % et /
3.5. Les opérateurs <<, >> et >>>
3.6. L'opérateur instanceof
3.7. Les opérateurs &, | et ^
3.8. Les opérateurs && et ||
3.9. L'opérateur ? ... :
3.10. Les opérateurs d'affectation
4. Tableaux
4.1. Création d'un tableau
4.2. Initialisation d'un tableau
4.3. Utilisation d'un tableau comme un Object
4.4. Tableaux de tableaux
4.5. Copie de tableaux
5. Blocs, boucles et contrôles
5.1. Blocs
5.2. Mots-clés réservés
5.3. Tests : if et switch
5.4. Boucles : for, while, do ... while
5.5. Commandes continue et break
5.6. Commandes return et goto
Nombres, précision, calculs
1. Introduction
2. Calculs
2.1. Précision
2.2. Codage des nombres flottants
2.3. Le mot-clé strictfp
2.4. Conversion de types
3. Dépassements de capacité
3.1. Cas des entiers
3.2. Cas des flottants
3.3. Bibliothèques BigInteger et BigDecimal
4. Fonctions mathématiques
4.1. Fonctions usuelles
4.2. Générateurs aléatoires
5. Classes enveloppe
5.1. Associer les types de base à des objets
5.2. Auto-boxing
Héritage, abstraction, interfaces
1. Introduction
2. Abstraction et encapsulation
2.1. Abstraction
2.2. Encapsulation
3. Héritage
3.1. Définition de l'héritage
3.2. Conséquences pour les membres
3.3. Polymorphisme
3.4. Empêcher l'héritage
4. Classes abstraites
5. Interfaces
5.1. Introduction
5.2. Définition
5.3. Java 8 et les interfaces
5.4. Utilisation des interfaces
5.5. Définition de constantes dans les interfaces
5.6. Utilité des interfaces
Packages
1. Introduction
2. Notion de paquet
2.1. Déclaration d’appartenance à un paquet
2.2. Chargement d’une classe
2.3. Choix de nom
3. Archives, chemin de recherche, classpath
3.1. Archives
3.2. Variable CLASSPATH
3.3. Notion de classloader
3.4. Bilan sur les classes chargées
3.5. Visibilité
3.6. Conseils d'écriture, bibliothèque standard
Exceptions
1. Introduction
2. Erreurs et Exceptions
2.1. Classe Throwable, notion de stack trace
2.2. Classe Error
2.3. Classe RuntimException
2.4. Classe Exception
3. Déclenchement d'une exception
3.1. Exceptions déclenchées par la JVM
3.2. Exceptions déclenchées par l'application
4. Capter une exception
4.1. Traiter une exception localement
4.2. Code de captage
5. Créer ses propres exceptions
Entrées / sorties
1. Introduction
2. Notion de fichier
2.1. Introduction
2.2. La classe File
2.3. Construction d'une instance de File
2.4. Méthodes exposées
3. Flux de sortie
3.1. Introduction, notion de flux
3.2. Écriture de caractères, classe Writer
3.3. Bufferisation, construction d'un flux sur un autre
3.4. Utilisation de PrintWriter
3.5. Écriture d'octets, OuputStream
3.6. Écriture de types primitifs : DataOutputStream
3.7. Écriture d'objets : ObjectOutputStream
4. Flux d'entrée
4.1. Introduction
4.2. Lecture de caractères, classe Reader
4.3. Bufferisation, lecture ligne par ligne
4.4. Lecture d'octets : InputStream
4.5. Lecture de types primitifs : DataInputStream
4.6. Lecture d'objets : ObjectInputStream
5. Lecture et écriture de flux croisés
5.1. Introduction
5.2. Lire des caractères sur un flux binaire : classe InputStreamReader
5.3. Écrire des caractères sur un flux binaire : classe OutputStreamWriter
5.4. Remarque sur les jeux de caractères
6. Serialization d'objets
6.1. Enjeu de la sérialization d'objets
6.2. Serialization d'un objet
6.3. Sérialization d'une grappe d'objets
6.4. Première surcharge : méthode writeObject() readObject()
6.5. Deuxième surcharge : utilisation d'un externalizer
6.6. Troisième surcharge : utilisation d'un objet proxy
7. Flux compressés
7.1. Introduction
7.2. Flux de type gzip
7.3. Flux de type zip