Wednesday, September 04, 2013

Como extrair um diretório de um Zip c/ Java

Exemplo de um programa simples, com Java 7, para extrair um diretório (e filhos se existir) contido em um arquivo zip (tar / war / ear / jar ...).
public class ExtractZipDir {
 
  public static void main(String[] args ) {
    if (args.length < 2) {
      System.out.println("Informe o arquivo zip e a pasta");
      return;
    }
    
    String zipName = args[0];
    String dirName =  args[1];
    extract(zipName, dirName);
  }
  
  static void extract(String zipName, String dirName) {
    long before = currentTimeMillis();
    try (ZipFile zip = new ZipFile(new File(zipName))) {
      ZipContent zContent = new ZipContent(zip);
      int files = zContent.extract(dirName);
      System.out.printf("Extraiu %s arquivos (e, %sms)", files,
        currentTimeMillis() - before);
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }
    
  private static class ZipContent {
    private final ZipFile zip;
    private Map<String, List<ZipEntry>> content = new HashMap<>();
    private int count;
    
    private static final String SEPARATOR = "/";
    private static final String ROOT_DIRECTORY = ".";
    private static final int BUFFER = 2048;
    
    private ZipContent (ZipFile zipFile) {
      zip = zipFile;
      organizeEntries(zip.entries());
    }
      
    private void organizeEntries(Enumeration<? extends ZipEntry> entries) {
      putRootDir();
       
      while (entries.hasMoreElements()) {
        ZipEntry entry = entries.nextElement();
        putEntry(entry);
      }
    }
      
    private void putEntry(ZipEntry entry) {
      String path = extractDirNameFromEntry(entry);
      List<ZipEntry> entries = content.get(path);
      if (entries == null) {
        content.put(path, new LinkedList<ZipEntry>());
      } else {
        entries.add(entry);
      }
    }
    
    private void putRootDir() {
      content.put(ROOT_DIRECTORY, new LinkedList<ZipEntry>());
    }
    
    private List<ZipEntry> listContent(String folder) {
      return content.get(folder);
    }
    
    private boolean hasDirectory(String folder) {
      return content.keySet().contains(folder);
    }
    
    private Queue<String> getAllDirectoriesFromParent(String dir) {
      Queue<String> q = new LinkedList<>();
      for (String d: content.keySet()) {
        if (d.contains(dir) && !d.equals(dir))
          q.add(d);
      }
      q.add(dir);
      return q;
    }
      
    private int extract(String dir) {
      if (!dir.equals(ROOT_DIRECTORY)) {
        if (!dir.endsWith(SEPARATOR)) {
          dir = dir.concat(SEPARATOR);
        }
        
        if (!hasDirectory(dir)) {
          throw new RuntimeException("Directory not found!");
        }
      }
      
      count = 0;
      Queue<String> dirs = getAllDirectoriesFromParent(dir);
      while (!dirs.isEmpty()) {
        String path = dirs.poll();
        List<ZipEntry> entries = listContent(path);
        File parent = new File(path);
        parent.mkdirs();
        
        for (ZipEntry entry: entries) {
          extract(parent, entry);
          count++;
        }
      }
      return count;
    }
      
    private void extract(File path, ZipEntry e) {
      String fileName = extractFilenameFromEntry(e);
      File destFile = new File(path, fileName);
      try (BufferedInputStream is = new BufferedInputStream(zip.getInputStream(e));
           FileOutputStream fos = new FileOutputStream(destFile);
           BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER)) {
        int currentByte;
        byte data[] = new byte[BUFFER];
        
        while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
          dest.write(data, 0, currentByte);
        }
        dest.flush();
      } catch (IOException e) {
        throw new RuntimeException("Não foi possível extrair!\n" +
          e.getMessage(), e);
      }
    }
    
    private static String extractDirNameFromEntry(ZipEntry entry) {
      String name = entry.getName();
      if (name.lastIndexOf(SEPARATOR) != -1) {
        return name.substring(0, name.lastIndexOf(SEPARATOR)+1);
      }
      return ROOT_DIRECTORY;
    }
    
    private static String extractFilenameFromEntry(ZipEntry entry) {
      String name = entry.getName();
      if (name.lastIndexOf(SEPARATOR) == -1) {
        return name;
      }
      return name.substring(name.lastIndexOf(SEPARATOR)+1);
    }
  }
}

Alguns formas de como executar esse programa (Linux / Windows):
$ java ExtractZipDir /home/user/meu.zip dir
$ java C:\usuario\Documents\meu.zip dir

Compartilhei esse código no github.

@edermag

No comments: