Pergunta

Como listar o conteúdo de arquivos ZIP ou JAR com uma aplicação Java?

Resposta

O formato ZIP, atualmente muito popular para o empacotamento e distribuição de software, é o mesmo utilizado pelos arquivos JAR (java archive). A classe java.util.zip.ZipFile permite a criação de um objeto que representa um arquivo ZIP possibilitando determinar o seu conteúdo, isto é, os arquivos existentes neste ZIP (ou JAR) sendo também possível a extração de um ou mais dos arquivos empacotados.

Para criar um objeto ZipFile referente a um arquivo basta fazer:

zip = new ZipFile(nomeArquivoZip);

Para obter-se as entradas existentes neste arquivo e processá-las:

Enumeration entries = zip.entries();
while (entries.hasMoreElements()) {
  // aqui processamos as entradas
  :
}

Cada entrada é um objeto da classe java.util.zip.ZipEntry que permite obter diversas informações sobre cada arquivo, tais como o tamanho do arquivo, o tamanho comprimido, o CRC ou método utilizado para compressão.

O exemplo abaixo permite todas listar as entradas existentes num arquivo ZIP e, opcionalmente, extrair um destes arquivos.

import java.io.*;
import java.util.Enumeration;
import java.util.zip.*;

public class ZipTool {
  public static final void main(String args[]) {
    boolean extractFile = false;
    int argIndex = 0;
    ZipFile zip = null;

    // Valida argumentos
    if (args.length==0) {
      usage();
    } else if (args[0].equals("-x")) {
      if (args.length>2) {
        extractFile = true;
        argIndex++;
      } else {
        usage();
      }
    }

    // Abre ZipFile
    try {
      zip = new ZipFile(args[argIndex]);
    } catch (ZipException e1) {
      System.out.println("Exception:\n" + e1.toString());
    } catch (IOException e2) {
      System.out.println("Exception:\n" + e2.toString());
    }

    // Obtem relacao de entradas do arquivo
    Enumeration entries = zip.entries();
    String dir = "";
    while (entries.hasMoreElements()) {
      ZipEntry ze = (ZipEntry)entries.nextElement();
      if (ze.isDirectory()) {
        dir = ze.getName();
        continue;
      } else if (extractFile) {
        if (ze.getName().equals(args[argIndex+1])) {
          // Extrai arquivo indicado
          try {
            extract(zip, ze);
          } catch (IOException e) {
            System.out.println("Can't extract " + ze.getName() + ":\n" + e);
          }
        }
      } else {
        System.out.println(dir + ze.getName() + " [size:" +
                           ze.getSize() + ", comp:" +
                           ze.getCompressedSize() + "]");
      }
    }
  }

  public static void usage() {
    System.out.println("Usage:\nZipTool [-x] zipfile file");
    System.exit(1);
  }

  public static void extract(ZipFile zf, ZipEntry ze)
                     throws IOException  {
    System.out.println("Extracting " + ze.getName());
    // Criando streams
    BufferedInputStream zipInput =
      new BufferedInputStream(zf.getInputStream(ze));
    BufferedOutputStream fileOutput =
      new BufferedOutputStream(new FileOutputStream(ze.getName()));
    // Obtendo tamanho do arquivo
    int sz = (int)ze.getSize();
    // Extracao do arquivo
    final int N = 1024;
    byte buf[] = new byte[N];
    int ln = 0;
    while (sz > 0 &&  // necessario devido bug JDK1.1 e 1.2
          (ln = zipInput.read(buf, 0, Math.min(N, sz))) != -1) {
      fileOutput.write(buf, 0, ln);
      sz -= ln;
    }
    // Fechando streams
    zipInput.close();
    fileOutput.flush();
    fileOutput.close();
  }
}

Com algumas modificações neste exemplo é possível adicionar-se novos arquivos ao arquivo compactado ZIP ou outras funcionalidades.