Co to jest dobra biblioteka Java do rozpakowywania / rozpakowywania plików? [Zamknięte]


232

Spojrzałem na domyślną bibliotekę Zip dostarczaną z bibliotekami kompresji JDK i Apache i jestem z nich niezadowolony z 3 powodów:

  1. Są wzdęte i mają zły projekt API. Muszę napisać 50 wierszy danych wyjściowych tablicy bajtów kotła, danych wejściowych zip, wydzielić pliki i zamknąć odpowiednie strumienie oraz wyłapać wyjątki i samodzielnie przenieść bufory bajtów ? Dlaczego nie mogę mieć prostego interfejsu API, który wygląda tak Zipper.unzip(InputStream zipFile, File targetDirectory, String password = null)i Zipper.zip(File targetDirectory, String password = null)to po prostu działa?

  2. Wygląda na to, że rozpakowanie rozpakowanie niszczy metadane pliku i obsługa hasła jest zepsuta.

  3. Czy wszystkie biblioteki, które wypróbowałem, były 2-3 razy wolniejsze w porównaniu z narzędziami zip wiersza poleceń, które otrzymuję w systemie UNIX?

Dla mnie (2) i (3) to drobne punkty, ale naprawdę chcę dobrej testowanej biblioteki z interfejsem jednowierszowym.


13
Jeśli chodzi o nr 1, to dlatego, że nie wszyscy rozpakowują plik do katalogu. Jeśli jesteś zawsze przy użyciu tego samego wzoru, dlaczego nie wystarczy napisać klasę użytkową, która otacza jedną z innymi i robi co trzeba go i po prostu użyć które ?
Edward Thomson,

14
@EdwardThomson, ponieważ łatwiej jest korzystać z biblioteki niż pisać kod, testować kod i utrzymywać kod.
Zak.

11
@EdwardThomson: Twój argument jest nieprawidłowy. Spójrz na interfejs zip API Python: docs.python.org/3/library/zipfile . Potrzebujesz 1 linii kodu, aby spakować lub rozpakować pliki. Interfejsy API powinny bardzo dobrze obsługiwać ten typowy przypadek i nie mogę wymyślić żadnego przypadku użycia interfejsu API zip oprócz skompresowania lub rozpakowania.
pathikrit

7
@wrick: spakowanie pliku lub rozpakowanie pliku jest specjalnym przypadkiem spakowania lub rozpakowania strumienia. Jeśli Twój interfejs API nie pozwala mi napisać strumienia do niego, a zamiast tego zmusza mnie do zapisania strumienia do pliku tylko po to, abym mógł go przesłać do interfejsu API, wówczas interfejs API jest uszkodzony mózg.
Edward Thomson,

52
@EdwardThomson - Świetnie, więc spraw, aby biblioteka obsługiwała zarówno pliki, jak i strumienie. To strata czasu każdego - mojego, twojego, pytającego i wszystkich innych pracowników Google, którzy natkną się na to, że każdy z nas musi wdrożyć własne Narzędzia Zip. Tak jak jest SUCHO, tak i DROP - nie powtarzaj innych ludzi.
ArtOfWarfare

Odpowiedzi:


291

Wiem, że jest późno i jest wiele odpowiedzi, ale ten zip4j jest jedną z najlepszych bibliotek do zipowania, z których korzystałem. Jest prosty (bez kodu kotła) i może z łatwością obsługiwać pliki chronione hasłem.

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();
    }
}

Zależność Maven to:

<dependency>
    <groupId>net.lingala.zip4j</groupId>
    <artifactId>zip4j</artifactId>
    <version>1.3.2</version>
</dependency>

1
dostałem org.zeroturnaround.zip.ZipException: java.io.FileNotFoundException: images \ 001GL.JPG: otwarcie nie powiodło się: błąd EINVAL (nieprawidłowy argument)
Smit Patel

4
Czy to działa z systemem Android?
Ercan

1
Nie, nie działa dobrze z Androidem, nie obsługuje języka chińskiego.
Dhiraj Himani

3
Zip4J nie obsługuje odczytu zip ze strumienia wejściowego, tylko z dysku.
Renaud Cerrato

2
Strona wydaje się nie mieć javadoc.
JohnC

76

Z Apache Commons IO „s IOUtilsmożna to zrobić:

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);
        }
    }
  }
}

To wciąż jakiś kod, ale ma tylko 1 nieegzotyczną zależność: Commons-IO


1
@VitalySazanovich masz na myśli Java 7 ZipEntry.
Randy

2
Dzięki. Na końcu wymaga również zipFile.close ().
JoshuaD

4
dlaczego nie IOUtils.closeQuietly (obecnie)?
Juan Mendez,

2
@JuanMendez, ponieważ jeśli występują błędy przy zamykaniu, nie możesz być pewien, że plik został zapisany całkowicie i poprawnie. Ale oprócz normalności close()nie będzie bolało.
vadipp

3
To rozwiązanie jest podatny na ZipSlip (zip4j jest również wpływ )
Marcono1234

40

Wyodrębnij plik zip i wszystkie jego podfoldery, używając tylko 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());
    }

}

Pliki zip i wszystkie jego podfoldery:

 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();
        }
    }
}

7
Wezwania do zamknięcia powinny znajdować się przynajmniej w blokach „wreszcie”. Wyjątki nie są odpowiednio obsługiwane. -> Myślę, że to część tego, dlaczego OP poprosił o bibliotekę do użycia.

4
To za dużo kodu. Można to zrobić w 2 liniach.
Makky

/mnt/sdcard/final_unzip_data/Product_images\001GL.JPG: otwarcie nie powiodło się: EINVAL (niepoprawny argument)
Smit Patel

@Joe Michael Dzięki kolego za opublikowanie tego. To rozwiązuje mój problem. Dam +1 zaextractFolder(String zipFile,String extractFolder)
OO7

Ten kod nie zachowuje atrybutów plików i uprawnień ... jeśli użyjesz czegoś takiego do rozpakowania działającej aplikacji, bądź przygotowany na dziwne błędy dotyczące uprawnień do plików. Kosztowało mnie to tydzień bólu głowy.
Renato

23

Inną opcją, którą możesz sprawdzić, jest zt-zip dostępny w centrali Maven i na stronie projektu pod adresem https://github.com/zeroturnaround/zt-zip

Posiada standardową funkcję pakowania i rozpakowywania (w strumieniach i systemie plików) + wiele metod pomocniczych do testowania plików w archiwum lub dodawania / usuwania wpisów.


17

Pełna implementacja do rozpakowywania / rozpakowywania folderu / pliku za pomocą zip4j


Pobierz słoik stąd i dodaj go do ścieżki kompilacji projektu. Poniżej classmożna skompresować i wyodrębnić dowolny plik lub folder, z lub bez ochrony hasłem-

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);
    }/**/
}

1
Ładna odpowiedź i biblioteka. Wyodrębnienie 1868 plików zajęło ~ 15 sekund w tej bibliotece, w porównaniu do ponad 20 minut przy użyciu ZipInputStream (z jakiegoś powodu)
Jonty800

8

Bardzo fajnym projektem jest TrueZip .

TrueZIP to oparta na Javie struktura wtyczek do wirtualnych systemów plików (VFS), która zapewnia przejrzysty dostęp do plików archiwalnych, tak jakby były zwykłymi katalogami

Na przykład (ze strony internetowej ):

File file = new TFile("archive.tar.gz/README.TXT");
OutputStream out = new TFileOutputStream(file);
try {
   // Write archive entry contents here.
   ...
} finally {
   out.close();
}

Biblioteka wygląda ładnie - wciąż nie jest oczywiste, jak po prostu rozpakować plik zip, biorąc pod uwagę zipinputstream / file / path.
pathikrit

1
Wydaje się, że TrueZIP nie radzi sobie dobrze z odczytem ze strumieni.
Teo Klestrup Röijezon

5
Czy nie jest to w dużej mierze to samo, co możesz zrobić w Javie 7? (spójrz na ZipFileSystemProvider ).
peterh

1
@peterh: Standardowy JDK ZipFileSystemProvider byłby dobrą odpowiedzią. Tylko kilka osób uważa to za komentarz.
iuzuz

3

Inną opcją jest JZlib . Z mojego doświadczenia wynika, że ​​jest mniej „wyśrodkowany na plikach” niż zip4J, więc jeśli chcesz pracować nad obiektami BLOB w pamięci, a nie plikami, możesz rzucić na to okiem.



0

Czy rzuciłeś okiem na http://commons.apache.org/vfs/ ? Upraszcza to dla ciebie wiele rzeczy. Ale nigdy nie użyłem tego w projekcie.

Nie jestem również świadomy bibliotek kompresji Java-Native innych niż JDK lub Apache Compression.

Pamiętam, jak raz zebraliśmy niektóre funkcje z Apache Ant - mają wbudowane wiele narzędzi do kompresji / dekompresji.

Przykładowy kod z VFS wyglądałby następująco:

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();
}

1
Wygląda na to, że sama obsługa plików zip w VFS jest dość ograniczona: commons.apache.org/vfs/filesystems.html
TJ Crowder
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.