Je viens de tomber sur un problème bête en Java, j'ai une application client/serveur avec un client qui envoie régulièrement des fichiers de logs au serveur sous forme de fichier ZIP. Le serveur doit décompresser les fichiers afin de la analyser pour en tirer des statistiques. Ce qui m'embêtait dans ce développement c'est qu'il faut dézipper les fichiers avant de pouvoir les analyser et que ce "dézippage" pourrait éventuellement laisser trainer des fichiers temporaires quelque part sur le serveur. Il me fallait donc une autre solution. J'en voyais 2 :

  • soit concatener les fichiers de logs en un seul fichier qu'il aurait suffit de gzipper pour pouvoir le lire avec un GZIPInputStream côté serveur.
  • soit permettre au serveur de lire de manière transparente les données de tous les fichiers contenus dans le zip.
La première solution aurait impliqué de faire des modifications sur le client qui utilise un système déjà existant (et standard pour notre application) pour envoyer ses fichiers ZIP, donc pas top. La seconde pose problème parce que le JDK ne fournit qu'un ZipInputStream qui permet de lire le Zip fichier par fichier, il faut en effet faire un appel à ZipInputStream.getNextEntry() entre chaque fichier pour passer à la suite. Comme l'ordre de lecture des fichiers n'a pas d'importance je décidais de faire un petit wrapper au stream afin qu'il soit capable de passer tout seul d'un fichier à l'autre. Et voilà le travail :

public static Reader acquireReader(File file) throws IOException {
       if (file.getName().endsWith(".zip")) {
           final ZipInputStream zis = new ZipInputStream(new FileInputStream(file));
           return new InputStreamReader(zis) {
               @Override
               public int read(char cbuf, int offset, int length) throws IOException {
                   int read = super.read(cbuf, offset, length);
                   if (read < length) {
                       if (read == -1) {
                           read = 0;
                       }
                       zis.getNextEntry();
                       read += super.read(cbuf, offset + read, length - read);
                   }
                   return read;
               }

               @Override
               public int read() throws IOException {
                   int c = super.read();
                   if (c == -1) {
                       zis.getNextEntry();
                       c = super.read();
                   }
                   return c;
               }
           };
       }
       return new FileReader(file);
   }

Comme vous pouvez le voire c'est relativement bateau :).