O que é uma boa biblioteca Java para compactar / descompactar arquivos? [fechadas]

232

Eu olhei para a biblioteca Zip padrão que acompanha as bibliotecas de compactação JDK e Apache e estou insatisfeita com elas por três razões:

  1. Eles estão inchados e têm um design de API ruim. Eu tenho que escrever 50 linhas de saída da matriz de bytes da placa da caldeira, entrada zip, arquivar fluxos e fechar fluxos relevantes, capturar exceções e mover os buffers de bytes por conta própria ? Por que não posso ter uma API simples que se parece com isso Zipper.unzip(InputStream zipFile, File targetDirectory, String password = null)e Zipper.zip(File targetDirectory, String password = null)que simplesmente funciona?

  2. Parece que descompactar descompacta destrói os metadados do arquivo e o tratamento da senha está quebrado.

  3. Além disso, todas as bibliotecas que tentei eram 2-3x mais lentas em comparação com as ferramentas de zip da linha de comando que recebo com o UNIX?

Para mim (2) e (3) são pontos menores, mas eu realmente quero uma boa biblioteca testada com uma interface de uma linha.

pathikrit
fonte
13
Quanto ao número 1, é porque nem todo mundo está simplesmente descompactando um arquivo em um diretório. Se você está sempre usando o mesmo padrão, por que não escrever uma classe de utilidade que envolva uma das outras e faça o que você precisa e use isso ?
Edward Thomson
14
@ EdwardThomson porque é mais fácil usar uma biblioteca do que escrever, testar e manter o código.
Zak
11
@EdwardThomson: Seu argumento é inválido. Veja a API zip do Python: docs.python.org/3/library/zipfile . Você precisa de 1 linha de código para compactar ou descompactar arquivos. As APIs devem lidar muito bem com o caso comum e não consigo pensar em nenhum caso de uso de uma API zip além de compactar ou descompactar.
Pathikrit
7
@wrick: compactar ou descompactar um arquivo é um caso especial de compactar ou descompactar um fluxo. Se sua API não me permite escrever um fluxo para ele e, em vez disso, me faz escrever um fluxo em um arquivo apenas para que eu possa alimentá-lo com sua API, sua API está com lesão cerebral.
Edward Thomson
52
@ EdwardThomson - Ótimo, então faça com que a biblioteca ofereça suporte a arquivos e fluxos. É uma perda de tempo de todo mundo - o meu, o seu, o autor da pergunta e todos os outros Googlers que se depararão com isso, pois cada um de nós precisa implementar nossos próprios utilitários Zip. Assim como existe o DRY, existe o DROP - Don't Repeat Other People.
ArtOfWarfare

Respostas:

291

Sei que é tarde e há muitas respostas, mas este zip4j é uma das melhores bibliotecas para compactar que já usei. É simples (sem código de caldeira) e pode lidar facilmente com arquivos protegidos por senha.

import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.core.ZipFile;


public static void unzip(){
    String source = "some/compressed/file.zip";
    String destination = "some/destination/folder";
    String password = "password";

    try {
         ZipFile zipFile = new ZipFile(source);
         if (zipFile.isEncrypted()) {
            zipFile.setPassword(password);
         }
         zipFile.extractAll(destination);
    } catch (ZipException e) {
        e.printStackTrace();
    }
}

A dependência do Maven é:

<dependency>
    <groupId>net.lingala.zip4j</groupId>
    <artifactId>zip4j</artifactId>
    <version>1.3.2</version>
</dependency>
user2003470
fonte
1
Eu tenho org.zeroturnaround.zip.ZipException: java.io.FileNotFoundException: images \ 001GL.JPG: aberto falhou: erro EINVAL (argumento inválido)
Smit Patel
4
Funciona com Android?
Ercan
1
Não, não está funcionando bem com o Android, não está suportando o idioma chinês.
Dhiraj Himani
3
O Zip4J não suporta a leitura de um zip de um fluxo de entrada, apenas do disco.
Renaud Cerrato
2
O site parece não ter um javadoc.
JohnC
76

Com o Apache Commons-IO , IOUtilsvocê pode fazer o seguinte:

try (java.util.zip.ZipFile zipFile = new ZipFile(file)) {
  Enumeration<? extends ZipEntry> entries = zipFile.entries();
  while (entries.hasMoreElements()) {
    ZipEntry entry = entries.nextElement();
    File entryDestination = new File(outputDir,  entry.getName());
    if (entry.isDirectory()) {
        entryDestination.mkdirs();
    } else {
        entryDestination.getParentFile().mkdirs();
        try (InputStream in = zipFile.getInputStream(entry);
             OutputStream out = new FileOutputStream(entryDestination)) {
            IOUtils.copy(in, out);
        }
    }
  }
}

Ainda é algum código padrão, mas possui apenas uma dependência não exótica: Commons-IO

Geoffrey De Smet
fonte
1
@VitalySazanovich você está se referindo ao Java 7 ZipEntry.
Randy
2
Obrigado. Também precisa de zipFile.close () no final.
27415 JoshuaD
4
por que não o IOUtils.closeQuietly (fora)?
Juan Mendez
2
@JuanMendez porque, se houver erros ao fechar, você não pode ter certeza de que o arquivo foi salvo inteiramente e corretamente. Mas, além do normal close(), não vai doer.
vadipp
3
Esta solução é vulnerável a ZipSlip (zip4j também é afectada )
Marcono1234
40

Extraia o arquivo zip e todas as suas subpastas, usando apenas o JDK:

private void extractFolder(String zipFile,String extractFolder) 
{
    try
    {
        int BUFFER = 2048;
        File file = new File(zipFile);

        ZipFile zip = new ZipFile(file);
        String newPath = extractFolder;

        new File(newPath).mkdir();
        Enumeration zipFileEntries = zip.entries();

        // Process each entry
        while (zipFileEntries.hasMoreElements())
        {
            // grab a zip file entry
            ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();
            String currentEntry = entry.getName();

            File destFile = new File(newPath, currentEntry);
            //destFile = new File(newPath, destFile.getName());
            File destinationParent = destFile.getParentFile();

            // create the parent directory structure if needed
            destinationParent.mkdirs();

            if (!entry.isDirectory())
            {
                BufferedInputStream is = new BufferedInputStream(zip
                .getInputStream(entry));
                int currentByte;
                // establish buffer for writing file
                byte data[] = new byte[BUFFER];

                // write the current file to disk
                FileOutputStream fos = new FileOutputStream(destFile);
                BufferedOutputStream dest = new BufferedOutputStream(fos,
                BUFFER);

                // read and write until last byte is encountered
                while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
                    dest.write(data, 0, currentByte);
                }
                dest.flush();
                dest.close();
                is.close();
            }


        }
    }
    catch (Exception e) 
    {
        Log("ERROR: "+e.getMessage());
    }

}

Arquivos zip e todas as suas subpastas:

 private void addFolderToZip(File folder, ZipOutputStream zip, String baseName) throws IOException {
    File[] files = folder.listFiles();
    for (File file : files) {
        if (file.isDirectory()) {
            addFolderToZip(file, zip, baseName);
        } else {
            String name = file.getAbsolutePath().substring(baseName.length());
            ZipEntry zipEntry = new ZipEntry(name);
            zip.putNextEntry(zipEntry);
            IOUtils.copy(new FileInputStream(file), zip);
            zip.closeEntry();
        }
    }
}
Bashir Beikzadeh
fonte
7
As chamadas para fechar devem estar dentro de blocos "finalmente", no mínimo. Exceções não são bem tratadas. -> Acho que é parte do motivo pelo qual o OP pediu uma biblioteca para usar.
4
Isso é muito código. Isso pode ser feito em 2 linhas.
24513 Makky
/mnt/sdcard/final_unzip_data/Product_images\001GL.JPG: aberto falhou: EINVAL (argumento inválido)
Smit Patel
@ Michael Michael Obrigado amigo por postar isso. Isso resolve meu problema. Eu vou te dar +1 paraextractFolder(String zipFile,String extractFolder)
OO7
Esse código não mantém atributos e permissões de arquivo ... se você usar algo como esse para descompactar um aplicativo executável, esteja preparado para erros estranhos em relação às permissões de arquivo. Isso me custou uma semana de dor de cabeça.
Renato
23

Outra opção que você pode conferir é o zt-zip disponível na página central e do projeto do Maven em https://github.com/zeroturnaround/zt-zip

Possui a funcionalidade padrão de compactação e descompactação (em fluxos e no sistema de arquivos) + vários métodos auxiliares para testar arquivos em um archive ou adicionar / remover entradas.

toomasr
fonte
17

Implementação completa para compactar / descompactar uma pasta / arquivo com zip4j


Faça o download do jar aqui e adicione-o ao caminho de construção do projeto. O classfole pode compactar e extrair qualquer arquivo ou pasta, com ou sem proteção por senha.

import java.io.File;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.util.Zip4jConstants;
import net.lingala.zip4j.core.ZipFile;  

public class Compressor {
    public static void zip(String targetPath, String destinationFilePath, String password) {
        try {
            ZipParameters parameters = new ZipParameters();
            parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);
            parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);

            if(password.length()>0){
                parameters.setEncryptFiles(true);
                parameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_AES);
                parameters.setAesKeyStrength(Zip4jConstants.AES_STRENGTH_256);
                parameters.setPassword(password);
            }

            ZipFile zipFile = new ZipFile(destinationFilePath);

            File targetFile = new File(targetPath);
            if(targetFile.isFile()){
                zipFile.addFile(targetFile, parameters);
            }else if(targetFile.isDirectory()){
                zipFile.addFolder(targetFile, parameters);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void unzip(String targetZipFilePath, String destinationFolderPath, String password) {
        try {
            ZipFile zipFile = new ZipFile(targetZipFilePath);
            if (zipFile.isEncrypted()) {
                zipFile.setPassword(password);
            }
            zipFile.extractAll(destinationFolderPath);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**/ /// for test only
    public static void main(String[] args) {

        String targetPath = "target\\file\\or\\folder\\path";
        String zipFilePath = "zip\\file\\Path"; 
        String unzippedFolderPath = "destination\\folder\\path";
        String password = "your_password"; // keep it EMPTY<""> for applying no password protection

        Compressor.zip(targetPath, zipFilePath, password);
        Compressor.unzip(zipFilePath, unzippedFolderPath, password);
    }/**/
}
Minhas Kamal
fonte
1
Uma boa resposta e biblioteca. Extraindo 1868 arquivos tomou ~ 15 segundos sobre esta biblioteca, em comparação com mais de 20 minutos, quando usando ZipInputStream (por alguma razão)
Jonty800
8

Um projeto muito bom é o TrueZip .

TrueZIP é uma estrutura de plug-in baseada em Java para sistemas de arquivos virtuais (VFS) que fornece acesso transparente a arquivos compactados como se fossem diretórios simples

Por exemplo (do site ):

File file = new TFile("archive.tar.gz/README.TXT");
OutputStream out = new TFileOutputStream(file);
try {
   // Write archive entry contents here.
   ...
} finally {
   out.close();
}
Michael
fonte
A biblioteca parece boa - ainda não é óbvio como descompactar um arquivo zip, dado um zipinputstream / file / path.
Pathikrit
1
O TrueZIP não parece lidar muito bem com a leitura de fluxos.
Teo Klestrup Röijezon
5
Não é basicamente o mesmo que você pode fazer no Java 7? (veja ZipFileSystemProvider ).
Peterh
1
@ Peterh: O ZipFileSystemProvider-JDK padrão seria uma boa resposta. Apenas poucas pessoas vêem isso como comentário.
Iuzuz 9/10
3

Outra opção é o JZlib . Na minha experiência, é menos "centrado em arquivos" que o zip4J; portanto, se você precisar trabalhar em blobs na memória em vez de arquivos, poderá dar uma olhada.

Henrik Aasted Sørensen
fonte
0

Você deu uma olhada em http://commons.apache.org/vfs/ ? Alega simplificar muitas coisas para você. Mas nunca o usei em um projeto.

Também não conheço outras bibliotecas de compactação Java-Native que não sejam o JDK ou o Apache Compression.

Lembro que, uma vez que retiramos alguns recursos do Apache Ant - eles têm muitos utilitários para compactação / descompactação incorporados.

O código de exemplo com o VFS seria semelhante a:

File zipFile = ...;
File outputDir = ...;
FileSystemManager fsm = VFS.getManager();
URI zip = zipFile.toURI();
FileObject packFileObject = fsm.resolveFile(packLocation.toString());
FileObject to = fsm.toFileObject(destDir);
FileObject zipFS;
try {
    zipFS = fsm.createFileSystem(packFileObject);
    fsm.toFileObject(outputDir).copyFrom(zipFS, new AllFileSelector());
} finally {
    zipFS.close();
}
wemu
fonte
1
Parece que o suporte para arquivos zip no material VFS por conta própria é bastante limitado: commons.apache.org/vfs/filesystems.html
TJ Crowder