DEV Community

Cover image for Zipar arquivos e pastas utilizando Java
Daniel Arrais
Daniel Arrais

Posted on

1

Zipar arquivos e pastas utilizando Java

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();
view raw block_one.java hosted with ❤ by GitHub

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:

  1. Primeiro criamos uma instância de File passando a URL do arquivo a ser compactado;
  2. Instanciamos nossos Streams, a FileOutputStream e a ZipOutputStream;
  3. Criamos o FileInputStream que lerá nosso arquivo, que é passado no construtor;
  4. Criamos uma instância ZipEntry passando o nome do arquivo no construtor e depois passamos ela para a ZipOutputStream por meio do método putNextEntry;
  5. Lemos os bytes do arquivo e gravamos eles, compactados, usando a nossa variável zipOutputStream;
  6. 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();
}
view raw block_two.java hosted with ❤ by GitHub

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();
}
view raw block_four.java hosted with ❤ by GitHub

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();
}
}
view raw block_five.java hosted with ❤ by GitHub

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, "");
}
}
view raw block_six.java hosted with ❤ by GitHub

Image of Datadog

The Essential Toolkit for Front-end Developers

Take a user-centric approach to front-end monitoring that evolves alongside increasingly complex frameworks and single-page applications.

Get The Kit

Top comments (1)

Collapse
 
wldomiciano profile image
Wellington Domiciano

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:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class Main {
  static void zip(final String zipName, final String... filesToZip) {
    try (final ZipOutputStream outStream = new ZipOutputStream(Files.newOutputStream(Paths.get(zipName)))) {
      Arrays.stream(filesToZip).forEach(fileName -> {
        try (final Stream<Path> paths = Files.walk(Paths.get(fileName))) {
          paths.filter(Files::isRegularFile).forEach(path -> {
            try {
              outStream.putNextEntry(new ZipEntry(path.toString()));
              outStream.write(Files.readAllBytes(path));
              outStream.closeEntry();
            } catch (final IOException e) {
              e.printStackTrace();
            }
          });
        } catch (final IOException e) {
          e.printStackTrace();
        }
      });
    } catch (final IOException e) {
      e.printStackTrace();
    }
  }

  public static void main(String... args) throws Exception {
    zip("only-one-file.zip", "simple-file.txt");
    zip("a-directory.zip", "simple-directory");
    zip("mixed.zip", "simple-file.txt", "simple-directory");
  }
}
Enter fullscreen mode Exit fullscreen mode

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 o ZipOutputStream. Até fiz este código para testar:

class Testando extends FileOutputStream {
  Testando() throws FileNotFoundException {
    super("simple-file.txt");
  }

  @Override
  public void close() throws IOException {
    System.out.println("FECHOU!!!");
    super.close();
  }

  public static void main(String... args) throws Exception {
    final ZipOutputStream outStream = new ZipOutputStream(new Testando());
    outStream.close();
  }
}
Enter fullscreen mode Exit fullscreen mode

Obrigado por compartilhar.

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay