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.