Zipar arquivos usando o Java é uma tarefas simples e não precisamos de bibliotecas de terceiros para isso. Para vermos
esta simplicidade na prática começarei o artigo implementando a zipagem de um arquivo, que também será utilizado na
zipagem de múltiplos arquivos e de pastas.
Zipar um arquivo
O código necessário para zipar um arquivo é este:
File file=new File("/home/daniel/teste_zip/gettyimages-521637760-170667a.jpg"); | |
FileOutputStream fileOutputStream=new FileOutputStream("file.zip"); | |
ZipOutputStream zipOutputStream=new ZipOutputStream(fileOutputStream); | |
FileInputStream fileInputStream=new FileInputStream(file); | |
ZipEntry zipEntry=new ZipEntry(file.getName()); | |
zipOutputStream.putNextEntry(zipEntry); | |
byte[]bytes=new byte[1024]; | |
int length; | |
while((length=fileInputStream.read(bytes))>=0){ | |
zipOutputStream.write(bytes,0,length); | |
} | |
fileInputStream.close(); | |
zipOutputStream.close(); | |
fileOutputStream.close(); |
No código você pode notar que utilizei as classes java.io.FileOutputStream
, java.io.FileInputStream
,
java.util.zip.ZipOutputStream
e a java.util.zip.ZipEntry
. Elas possuem alguns papéis, a FileOutputStream
é
necessária para gravar bytes no disco e a FileInputStream
para ler; a ZipOutputStream
é uma FileOutputStream
especializada em criar e gravar arquivos zip's e dispõe de métodos que auxiliam tal tarefa; a ZipEntry
é utilizada
para adicionar arquivos dentro do ZIP, nela podemos adicionar informações sobre o arquivo, como o nome dele.
Como você deve ter notado (espero rsrsrsrs), o código é simples e consegue zipar um único arquivo. Nele não há segredos:
- Primeiro criamos uma instância de File passando a URL do arquivo a ser compactado;
- Instanciamos nossos Streams, a
FileOutputStream
e aZipOutputStream
; - Criamos o
FileInputStream
que lerá nosso arquivo, que é passado no construtor; - Criamos uma instância
ZipEntry
passando o nome do arquivo no construtor e depois passamos ela para aZipOutputStream
por meio do métodoputNextEntry
; - Lemos os bytes do arquivo e gravamos eles, compactados, usando a nossa variável
zipOutputStream
; - Por fim, fechamos os streams instanciados.
Pronto! Agora temos um código Java capaz de zipar um arquivo e você entende como ele funciona! Não pararemos por aqui e
nos próximos tópicos você verá soluções para compactar múltiplos arquivos e pastas! Porém, sugiro que antes de continuar
a leitura, tente melhorar o código proposto e implementarmos suas próprias soluções. Deixo apenas uma dica: na
compactação de pastas a recursividade pode ser uma aliada.
Zipar múltiplos arquivos
Antes de implementarmos a solução vamos encapsular o código anterior em um método com a seguinte
assinatura: void zipFile(File file, String zipName)
. A principal mudança fica a cargo do recebimento do arquivo e do
nome do zip como argumentos. Vamos encapsular também a parte do código que adiciona arquivos dentro do zip, ele terá a
seguinte assinatura: void addFileInZip(ZipOutputStream zipOutputStream, File file)
. Após essa refatoração nosso
primeiro código ficará como a seguir.
public static void zipFile(File file,String zipName)throws IOException{ | |
FileOutputStream fileOutputStream=new FileOutputStream(zipName); | |
ZipOutputStream zipOutputStream=new ZipOutputStream(fileOutputStream); | |
addFileInZip(zipOutputStream,file); | |
zipOutputStream.close(); | |
fileOutputStream.close(); | |
} |
public static void addFileInZip(ZipOutputStream zipOutputStream,File file)throws IOException{ | |
FileInputStream fileInputStream=new FileInputStream(file); | |
ZipEntry zipEntry=new ZipEntry(file.getName()); | |
zipOutputStream.putNextEntry(zipEntry); | |
byte[]bytes=new byte[1024]; | |
int length; | |
while((length=fileInputStream.read(bytes))>=0){ | |
zipOutputStream.write(bytes,0,length); | |
} | |
fileInputStream.close(); | |
} |
Agora que temos um método que adiciona um arquivo dentro do zip podemos criar o novo método que zipa vários. Nele temos
que receber uma lista de arquivos e o nome do zip, essa lista deverá ser iterada e ter cada item adicionado ao zip,
ficando assim:
public static void zipFiles(List<File> files, String zipName) throws IOException { | |
FileOutputStream fileOutputStream = new FileOutputStream(zipName); | |
ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream); | |
for (File file : files) { | |
addFileInZip(zipOutputStream, file); | |
} | |
zipOutputStream.close(); | |
fileOutputStream.close(); | |
} |
Zipar pastas
A zipagem de uma pasta é um pouco mais complexa - bem pouco mesmo - pois precisamos vasculhá-la em busca dos arquivos
para adicioná-los ao ZIP. Essa busca pode ser facilitada com uso de recursividade. Para isso vamos duplicar nosso
método addFileInZip
adicionando mais um parâmetro chamado parentPath
em sua assinatura. A ideia aqui é chamarmos ele
passando o arquivo/pasta e o nome do caminho deles, repetindo isso forma recursiva para cada arquivo ou pasta
encontrada. O método deve trabalhar receber tanto pastas quanto arquivos. Para melhor entendimento a seguir temos a
implementação do que foi comentado.
private static void addFileInZip(File file, ZipOutputStream zipOutputStream, String parentPath) throws IOException { | |
if (file == null || file.isDirectory() && file.listFiles() == null) { | |
return; | |
} | |
if (file.isDirectory()) { | |
for (File childFile : file.listFiles()) { | |
addFileInZip(childFile, zipOutputStream, parentPath + "/" + file.getName()); | |
} | |
} else { | |
FileInputStream fileInputStream = new FileInputStream(file); | |
ZipEntry zipEntry = new ZipEntry(parentPath + "/" + file.getName()); | |
zipOutputStream.putNextEntry(zipEntry); | |
byte[] bytes = new byte[1048576]; | |
int length; | |
while ((length = fileInputStream.read(bytes)) >= 0) { | |
zipOutputStream.write(bytes, 0, length); | |
} | |
fileInputStream.close(); | |
} | |
} |
Para verificar o File passado é um arquivo ou uma pasta utilizei o método isDirectory()
. Se for uma pasta eu itero os
arquivos dela - obtido por meio do método listFiles()
, chamando o método de forma recursiva e passando para ele o path
do arquivo/pasta concatendado com parentPath
. A concatenação é necessária para que ao chegar em um arquivo, o path
recebido seja completo. Caso seja um arquivo, ele adiciona o arquivo ao ZIP normalmente, concatenando a
váriavel parentPath
ao seu nome para que seu path dentro do zip fique correto.
E como você deve ter notado, agora o código do primeiro método addFileInZip
está duplicado. Para resolver isso você
pode substituir todo o código do addFileInZip
original por uma chamada do novo, passando no terceiro paramêtro uma
String vazia. Assim deixamos nosso código mais clean e manutenível.
Com essa refatoração no addFileInZip
podemos perceber que não precisamos criar um método exclusivo para compactação
de pasta, pois o zipFile
já vai ser capaz de tal tarefa. Porém, por questão de semântica, você pode renomêá-lo
para zipFileOrFolder
ou criar um método chamado zipFolder
que chama o zipFile
.
O resultado final pode ser visualizado abaixo:
import java.io.File; | |
import java.io.FileInputStream; | |
import java.io.FileOutputStream; | |
import java.io.IOException; | |
import java.util.Arrays; | |
import java.util.List; | |
import java.util.zip.ZipEntry; | |
import java.util.zip.ZipOutputStream; | |
public class ZipUtils { | |
public static void main(String[] args) throws IOException { | |
File fileOne = new File("/home/daniel/teste_zip/gettyimages-521637760-170667a.jpg"); | |
File fileTwo = new File("/home/daniel/teste_zip/gettyimages-540702166-170667a.jpg"); | |
File fileThree = new File("/home/daniel/teste_zip/gettyimages-1284117492-170667a.jpg"); | |
File folder = new File("/home/daniel/Downloads"); | |
zipFile(fileOne, "file"); | |
zipFiles(Arrays.asList(fileOne, fileTwo, fileThree), "files"); | |
zipFolder(folder, "folder"); | |
} | |
public static void zipFile(File file, String zipName) throws IOException { | |
FileOutputStream fileOutputStream = new FileOutputStream(zipName + ".zip"); | |
ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream); | |
addFileInZip(file, zipOutputStream); | |
zipOutputStream.close(); | |
fileOutputStream.close(); | |
} | |
private static void zipFiles(List<File> files, String zipName) throws IOException { | |
FileOutputStream fileOutputStream = new FileOutputStream(zipName + ".zip"); | |
ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream); | |
for (File file : files) { | |
addFileInZip(file, zipOutputStream); | |
} | |
zipOutputStream.close(); | |
fileOutputStream.close(); | |
} | |
private static void zipFolder(File folder, String zipName) throws IOException { | |
zipFile(folder, zipName); | |
} | |
private static void addFileInZip(File file, ZipOutputStream zipOutputStream, String parentPath) throws IOException { | |
if (file == null || file.isDirectory() && file.listFiles() == null) { | |
return; | |
} | |
if (file.isDirectory()) { | |
for (File childFile : file.listFiles()) { | |
addFileInZip(childFile, zipOutputStream, parentPath + "/" + file.getName()); | |
} | |
} else { | |
FileInputStream fileInputStream = new FileInputStream(file); | |
ZipEntry zipEntry = new ZipEntry(parentPath + "/" + file.getName()); | |
zipOutputStream.putNextEntry(zipEntry); | |
byte[] bytes = new byte[1048576]; | |
int length; | |
while ((length = fileInputStream.read(bytes)) >= 0) { | |
zipOutputStream.write(bytes, 0, length); | |
} | |
fileInputStream.close(); | |
} | |
} | |
private static void addFileInZip(File file, ZipOutputStream zipOutputStream) throws IOException { | |
addFileInZip(file, zipOutputStream, ""); | |
} | |
} |
Top comments (1)
Olá! Achei bem legal o seu post. Com base nele pesquisei mais um pouco e vi que dá pra simplificar bastante usando recursos do Java 7 e do Java 8 como o try-with-resources e as streams.
Veja como eu fiz:
Eu não testei exaustivamente, então, se vc testar e puder dizer se funcionou (ou se deu problema) para o seu caso de uso eu ficaria bem feliz.
Uma coisa interessante que vi foi que vc não precisa fechar o seu
FileOutputStream
porque ele é fechado automaticamente quando vc fecha oZipOutputStream
. Até fiz este código para testar:Obrigado por compartilhar.