Como compactar e descompactar arquivos com Java?

2009/09/25

A classe ZipUtils, apresentada a seguir, dispõe de dois métodos públicos estáticos: compress e extract.

Utilidade para ela não falta!

Eu por exemplo já criei programas que geram backups, fiz servlets que compactavam arquivos quando o cliente solicitava download, etc.

Abaixo um simples exemplo de como utilizá-la:

import java.io.*;

import br.com.staroski.tools.zip.*;

public class Exemplo {

    public static void main(String[] args) {
        try {

            // para compactar faça
            File origem = new File("caminho completo do arquivo ou diretório a ser compactado");
            File destino = new File("caminho completo do arquivo compactado a ser criado");
            System.out.println("compactando arquivo...");
            ZipUtils.compress(origem, destino);
            System.out.println("arquivo compactado com sucesso!");

            // para descompactar faça
            origem = new File("caminho completo do arquivo a ser descompactado");
            destino = new File("caminho do diretório onde o arquivo será descompactado");
            System.out.println("descompactando arquivo...");
            ZipUtils.extract(origem, destino);
            System.out.println("arquivo descompactado com sucesso!");

        } catch (Throwable t) {
            t.printStackTrace();
        }
    }
}

Aqui está o fonte da classe, recomendo baixar diretamente pelo GitHub.

package br.com.staroski.tools.zip;

import static br.com.staroski.tools.io.IO.*;

import java.io.*;
import java.nio.file.*;
import java.util.zip.*;

/**
 * Classe utilitária para compactação e descompactação de arquivos ZIP
 * 
 * @author Ricardo Artur Staroski
 */
public final class ZipUtils {

    /**
     * Compacta determindado arquivo ou diretório para o arquivo ZIP
     * especificado
     * 
     * @param input
     *            O arquivo ou diretório de entrada
     * @param output
     *            O arquivo ZIP de saída
     *
     *@return O checksum da compactação do arquivo
     */
    public static long compress(final File input, final File output) throws IOException {
        if (!input.exists()) {
            throw new IOException(input.getName() + " não existe!");
        }
        if (output.exists()) {
            if (output.isDirectory()) {
                throw new IllegalArgumentException("\"" + output.getAbsolutePath() + "\" não é um arquivo!");
            }
        } else {
            final File parent = output.getParentFile();
            if (parent != null) {
                parent.mkdirs();
            }
            output.createNewFile();
        }
        Checksum checksum = createChecksum();
        final ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(output));
        zip.setLevel(Deflater.BEST_COMPRESSION);
        compressInternal(null, input, zip, checksum);
        zip.finish();
        zip.flush();
        zip.close();
        return checksum.getValue();
    }

    /**
     * Extrai um arquivo ZIP para o diretório especificado
     * 
     * @param input
     *            O arquivo ZIP de entrada
     * @param output
     *            O diretório de saída
     *@return O checksum da descompactação do arquivo
     */
    public static long extract(final File input, final File output) throws IOException {
        if (input.exists()) {
            if (input.isDirectory()) {
                throw new IllegalArgumentException("\"" + input.getAbsolutePath() + "\" não é um arquivo!");
            }
        } else {
            throw new IllegalArgumentException("\"" + input.getAbsolutePath() + "\" não existe!");
        }
        if (output.exists()) {
            if (output.isFile()) {
                throw new IllegalArgumentException("\"" + output.getAbsolutePath() + "\" não é um diretório!");
            }
        }
        Checksum checksum = createChecksum();
        final ZipInputStream zip = new ZipInputStream(new FileInputStream(input));
        extractInternal(zip, output, checksum);
        zip.close();
        return checksum.getValue();
    }

    // Adiciona determinado arquivo ao ZIP
    private static void compressInternal(final String caminho, final File arquivo, final ZipOutputStream zip, Checksum checksum) throws IOException {
        final boolean dir = arquivo.isDirectory();
        String nome = arquivo.getName();
        nome = (caminho != null ? caminho + "/" + nome : nome);
        final ZipEntry item = new ZipEntry(nome + (dir ? "/" : ""));
        item.setTime(arquivo.lastModified());
        zip.putNextEntry(item);
        if (dir) {
            zip.closeEntry();
            final File[] arquivos = arquivo.listFiles();
            for (int i = 0; i < arquivos.length; i++) {
                // recursivamente adiciona outro arquivo ao ZIP
                compressInternal(nome, arquivos[i], zip, checksum);
            }
        } else {
            item.setSize(arquivo.length());
            final FileInputStream entrada = new FileInputStream(arquivo);
            copy(entrada, zip, checksum);
            entrada.close();
            zip.closeEntry();
        }
    }

    private static Checksum createChecksum() {
        return new CRC32();
    }

    // Retira determinado elemento do arquivo ZIP
    private static void extractInternal(final ZipInputStream zip, final File pasta, Checksum checksum) throws IOException {
        ZipEntry elemento = null;
        while ((elemento = zip.getNextEntry()) != null) {
            String nome = elemento.getName();
            nome = nome.replace('/', File.separatorChar);
            nome = nome.replace('\\', File.separatorChar);
            File arquivo = new File(pasta, nome);
            if (elemento.isDirectory()) {
                arquivo.mkdirs();
            } else {
                boolean existe = arquivo.exists();
                if (!existe) {
                    final File parent = arquivo.getParentFile();
                    if (parent != null) {
                        parent.mkdirs();
                    }
                    arquivo.createNewFile();
                }
                boolean oculto = false;
                boolean somenteLeitura = false;
                if (existe) {
                    oculto = arquivo.isHidden();
                    if (oculto) {
                        Files.setAttribute(arquivo.toPath(), "dos:hidden", false);
                    }
                    somenteLeitura = !arquivo.canWrite();
                    if (somenteLeitura) {
                        arquivo.setWritable(true);
                    }
                }

                OutputStream saida = new FileOutputStream(arquivo);
                copy(zip, saida, checksum);
                saida.close();

                if (existe) {
                    if (somenteLeitura) {
                        arquivo.setWritable(false);
                    }
                    if (oculto) {
                        Files.setAttribute(arquivo.toPath(), "dos:hidden", true);
                    }
                }
            }
            arquivo.setLastModified(elemento.getTime());
        }
    }

    // Construtor privado - Náo há razão em instanciar esta classe
    private ZipUtils() {
    }
}

Se você não quiser baixar do GitHub, então terá de copiar a classe abaixo também pois a ZipUtils depende dela:

package br.com.staroski.tools.io;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.*;

/**
 * Classe utilit&aacute;ria para opera&ccedil;&otilde;es de I/O
 * 
 * @author Ricardo Artur Staroski
 */
public final class IO {

    /**
     * Copia o arquivo de origem para o arquivo de destino.
     * 
     * @param from
     *            O arquivo de origem.
     * @param to
     *            O arquivo de destino.
     * @throws IOException
     */
    public static void copy(File from, File to) throws IOException {
        InputStream in = new FileInputStream(from);
        OutputStream out = new FileOutputStream(to);
        copy(in, out);
        in.close();
        out.close();
        to.setLastModified(from.lastModified());
    }

    /**
     * Copia o arquivo de origem para o arquivo de destino.
     * 
     * @param from
     *            O arquivo de origem.
     * @param to
     *            O arquivo de destino.
     * @param checksum
     *            O checksum da escrita.
     * @throws IOException
     */
    public static void copy(File from, File to, Checksum checksum) throws IOException {
        InputStream in = new FileInputStream(from);
        OutputStream out = new FileOutputStream(to);
        copy(in, out, checksum);
        in.close();
        out.close();
        to.setLastModified(from.lastModified());
    }

    /**
     * Copia o conte&uacute;do do stream de entrada para o stream de sa&iacute;da.
     * 
     * @param from
     *            O stream de entrada.
     * @param to
     *            O stream de sa&iacute;da.
     * @throws IOException
     */
    public static void copy(InputStream from, OutputStream to) throws IOException {
        final int count = BLOCK_SIZE;
        byte[] bytes = new byte[count];
        for (int read = -1; (read = from.read(bytes, 0, count)) != -1; to.write(bytes, 0, read))
            ;
        to.flush();
    }

    /**
     * Copia o conte&uacute;do do stream de entrada para o stream de sa&iacute;da.
     * 
     * @param from
     *            O stream de entrada.
     * @param to
     *            O stream de sa&iacute;da.
     * @param checksum
     *            O checksum da escrita.
     * @throws IOException
     */
    public static void copy(InputStream from, OutputStream to, Checksum checksum) throws IOException {
        final int count = BLOCK_SIZE;
        byte[] bytes = new byte[count];
        for (int read = -1; (read = from.read(bytes, 0, count)) != -1; to.write(bytes, 0, read)) {
            checksum.update(bytes, 0, read);
        }
        to.flush();
    }

    /**
     * Copia o arquivo de origem para o arquivo de destino.
     * 
     * @param from
     *            O caminho do arquivo de origem.
     * @param to
     *            O caminho do arquivo de destino.
     * @throws IOException
     */
    public static void copy(String from, String to) throws IOException {
        copy(new FileInputStream(from), new FileOutputStream(to));
    }

    /**
     * Copia o arquivo de origem para o arquivo de destino.
     * 
     * @param from
     *            O caminho do arquivo de origem.
     * @param to
     *            O caminho do arquivo de destino.
     * @param checksum
     *            O checksum da escrita.
     * @throws IOException
     */
    public static void copy(String from, String to, Checksum checksum) throws IOException {
        copy(new FileInputStream(from), new FileOutputStream(to), checksum);
    }

    /**
     * Analisa o arquivo informado, se o mesmo n&atilde;o existir, um novo &eacute; criado
     * 
     * @param file
     *            O arquivo a ser verificado
     * @return O pr&oacute;prio par&acirc;metro
     * @throws IOException
     */
    public static File createIfNotExists(File file) throws IOException {
        if (!file.exists()) {
            File parent = file.getParentFile();
            if (parent != null) {
                parent.mkdirs();
            }
            file.createNewFile();
        }
        return file;
    }

    /**
     * Apaga o arquivo informado
     * 
     * @param file
     *            O arquivo a ser apagado
     * @throws IOException
     */
    public static void delete(File file) throws IOException {
        Files.deleteIfExists(file.toPath());
    }

    /**
     * Apaga o arquivo informado
     * 
     * @param file
     *            O arquivo a ser apagado
     * @throws IOException
     */
    public static void delete(String file) throws IOException {
        delete(new File(file));
    }

    /**
     * Obt&eacute;m todas as linhas do arquivo informado
     * 
     * @param file
     *            O arquivo do qual se deseja ler as linhas
     * @return Uma lista de contendo as linhas do arquivo
     * @throws IOException
     */
    public static List<String> readLines(File file) throws IOException {
        List<String> lines = new ArrayList<String>();
        BufferedReader input = new BufferedReader(new FileReader(file));
        String line = null;
        while ((line = input.readLine()) != null) {
            lines.add(line);
        }
        input.close();
        return lines;
    }

    /**
     * Grava as linhas no arquivo informado
     * 
     * @param lines
     *            As linhas a serem gravadas
     * @param file
     *            O arquivo no qual se deseja gravar as linhas
     * @throws IOException
     */
    public static void writeLines(File file, List<String> lines) throws IOException {
        BufferedWriter output = new BufferedWriter(new FileWriter(file));
        for (int i = 0, n = lines.size(); i < n; i++) {
            if (i > 0) {
                output.newLine();
            }
            output.write(lines.get(i));
        }
        output.flush();
        output.close();
    }

    /**
     * Tamanho padr&atilde;o, 8KB, utilizado para blocos de mem&oacute;ria.
     */
    public static int BLOCK_SIZE = 8192;

    // não faz sentido instanciar esta classe
    private IO() {
    }
}