Zip/Unzip em Java



Zip/Unzip em Java

Colaboração: Carlos Tosin (instrutor oficial do Curso On-Line de Fundamentos de Java da Softblue)

A compactação de dados está amplamente presente em sistemas, serviços e aplicativos. Quem nunca precisou compactar arquivos para mandar para alguém, ou então compactar dados para trafegarem pela rede?

Neste tópico apresentarei uma maneira fácil para compactar e descompactar arquivos no formato ZIP utilizando Java. Java possui suporte a arquivos ZIP, logo não é necessário utilizar nenhuma API extra. As classes estão localizadas dentro do pacote java.util.zip. As classes principais desse pacote são: ZipEntry, ZipFile, ZipInputStream e ZipOutputStream. Para demonstrar como a compactação e descompactação funcionam, vou mostrar a criação de uma classe com dois métodos: zip() e unzip(). Todo o "trabalho sujo" fica por conta da classe. O código-fonte pode ser visualizado no final desta explicação.

Vamos começar pelo método zip(). O método zip() recebe dois parâmetros: o primeiro é a lista de arquivos a serem zipados; e o segundo é o arquivo ZIP que será gerado. O código deste método é bastante simples porque toda a lógica de compactação se encontra no método zipFiles().

O método zipFiles() é reponsável por iterar sobre a lista de arquivos e adicioná-los ao arquivo ZIP de saída. Esta tarefa seria simples, mas às vezes queremos compactar não apenas arquivos, mas também estruturas de diretórios dentro do nosso arquivo ZIP. E para manter essa estrutura de diretórios de forma correta, devemos programar este comportamento manualmente.

Observe que, caso uma das entradas que deve aparecer no arquivo ZIP seja um diretório, o método zipFiles() é chamado recursivamente, passando como parâmetro a lista de arquivos do diretório. Esta abordagem possibilita que o método processe todos os arquivos de cada diretório, de uma forma semelhante à busca em profundidade que aprendemos nas aulas de Estruturas de Dados. Junto com a lista de arquivos, também é fornecida uma pilha com os nomes dos diretórios onde o arquivo se encontra. Essa informação é utilizada na reconstrução do caminho do arquivo dentro do arquivo ZIP.

Já para descompactar, temos o método unzip(). O método unzip() recebe dois parâmetros: o arquivo ZIP a ser descompactado e um diretório para a descompactação. No caso da descompactação é feito o caminho inverso da compactação. Cada entrada do arquivo ZIP é lida e gravada no sistema de arquivos. Caso a entrada seja um diretório, a estrutura de diretórios deve primeiramente ser criada e só então o arquivo deve ser descompactado (o Java não cria os diretórios automaticamente, ficando a cargo do programador garantir a criação dos diretórios necessários).

Esses dois métodos representam uma solução básica e bem completa para compactar e descompactar arquivos. Recomendo que você inclua esta funcionalidade em um componente, a fim de que possa ser usado em diferentes projetos.

Carlos Tosin é instrutor oficial do Curso On-Line de Fundamentos de Java da Softblue, formado em Ciência da Computação pela PUC-PR, pós-graduado em Desenvolvimento de Jogos para Computador pela Universidade Positivo e Mestre em Informática na área de Sistemas Distribuídos, também pela PUC-PR. Trabalha profissionalmente com Java há 7 anos e possui 4 anos de experiência no desenvolvimento de sistemas para a IBM dos Estados Unidos, utilizados a nível mundial. Atua há mais de 2 anos com cursos e treinamentos de profissionais em grandes empresas. Possui as certificações da Sun SCJP, SCJD, SCWCD, SCBCD, SCEA, IBM SOA e ITIL Foundation.


Código-fonte utilizado no artigo:

package zip;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Stack;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

public class ZipHelper {

public void zip(File[] files, File outputFile) throws IOException {

if (files != null && files.length > 0) {
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(outputFile));
Stack parentDirs = new Stack();
zipFiles(parentDirs, files, out);
out.close();
}
}

private void zipFiles(Stack parentDirs, File[] files, ZipOutputStream out) throws IOException {
byte[] buf = new byte[1024];

for (int i = 0; i < files.length; i++) {
if (files[i].isDirectory()) {
//se a entrad é um diretório, empilha o diretório e chama o mesmo método recursivamente
parentDirs.push(files[i]);
zipFiles(parentDirs, files[i].listFiles(), out);

//após processar as entradas do diretório, desempilha
parentDirs.pop();
} else {
FileInputStream in = new FileInputStream(files[i]);

//itera sobre os itens da pilha para montar o caminho completo do arquivo
String path = "";
for(File parentDir : parentDirs) {
path += parentDir.getName() + "/";
}

//grava os dados no arquivo zip
out.putNextEntry(new ZipEntry(path + files[i].getName()));

int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}

out.closeEntry();
in.close();
}
}
}

public void unzip(File zipFile, File dir) throws IOException {
ZipFile zip = null;
File arquivo = null;
InputStream is = null;
OutputStream os = null;
byte[] buffer = new byte[1024];

try {
// cria diretório informado, caso não exista
if (!dir.exists()) {
dir.mkdirs();
}
if (!dir.exists() || !dir.isDirectory()) {
throw new IOException("O diretório " + dir.getName() + " não é um diretório válido");
}

zip = new ZipFile(zipFile);
Enumeration e = zip.entries();
while (e.hasMoreElements()) {
ZipEntry entrada = (ZipEntry) e.nextElement();
arquivo = new File(dir, entrada.getName());

// se for diretório inexistente, cria a estrutura e pula pra próxima entrada
if (entrada.isDirectory() && !arquivo.exists()) {
arquivo.mkdirs();
continue;
}

// se a estrutura de diretórios não existe, cria
if (!arquivo.getParentFile().exists()) {
arquivo.getParentFile().mkdirs();
}
try {
// lê o arquivo do zip e grava em disco
is = zip.getInputStream(entrada);
os = new FileOutputStream(arquivo);
int bytesLidos = 0;
if (is == null) {
throw new ZipException("Erro ao ler a entrada do zip: " + entrada.getName());
}
while ((bytesLidos = is.read(buffer)) > 0) {
os.write(buffer, 0, bytesLidos);
}
} finally {
if (is != null) {
try {
is.close();
} catch (Exception ex) {
}
}
if (os != null) {
try {
os.close();
} catch (Exception ex) {
}
}
}
}
} finally {
if (zip != null) {
try {
zip.close();
} catch (Exception e) {
}
}
}
}
}

Autor: Carlos Eduardo Gusso Tosin


Artigos Relacionados


Utilizando A Api Cglib Para Interceptar Chamadas De Métodos Em Objetos Java

Memory Leak Em Java

Como Funciona Uma Fila Circular Dinamica

Sobre Seo

Gerenciamento De Projetos - Softwares Livres

A Ferramenta Cscl Claroline

Facebook Sdk (preview)