Apa perpustakaan Java yang baik untuk zip / unzip file? [Tutup]

232

Saya melihat perpustakaan Zip default yang datang dengan JDK dan lib kompresi Apache dan saya tidak senang dengan mereka karena 3 alasan:

  1. Mereka bengkak dan memiliki desain API yang buruk. Saya harus menulis 50 baris output array byte boiler plate, input zip, file stream dan menutup stream yang relevan dan menangkap pengecualian dan memindahkan buffer byte sendiri ? Mengapa saya tidak dapat memiliki API sederhana yang terlihat seperti ini Zipper.unzip(InputStream zipFile, File targetDirectory, String password = null)dan Zipper.zip(File targetDirectory, String password = null)itu berfungsi?

  2. Tampaknya membuka zip merusak file meta-data dan penanganan kata sandi rusak.

  3. Juga, semua perpustakaan yang saya coba adalah 2-3x lambat dibandingkan dengan alat zip baris perintah yang saya dapatkan dengan UNIX?

Bagi saya (2) dan (3) adalah poin kecil tapi saya benar-benar ingin perpustakaan yang diuji dengan antarmuka satu baris.

pathikrit
sumber
13
Adapun # 1, itu karena tidak semua orang hanya membuka ritsleting file ke direktori. Jika Anda selalu menggunakan pola yang sama, mengapa tidak hanya menulis kelas utilitas yang membungkus salah satu dari yang lain dan melakukan apa yang Anda butuhkan untuk dan hanya menggunakan itu ?
Edward Thomson
14
@EdwardThomson karena lebih mudah menggunakan perpustakaan daripada menulis kode, menguji kode, dan memelihara kode.
Zak
11
@EdwardThomson: Argumen Anda tidak valid. Lihatlah API zip Python: docs.python.org/3/library/zipfile . Anda memerlukan 1 baris kode untuk zip atau unzip file. API harus menangani kasus umum dengan sangat baik dan saya tidak bisa memikirkan kasus penggunaan API zip selain zip atau unzipping.
pathikrit
7
@wrick: zip file atau membuka ritsleting file adalah kasus khusus ritsleting atau membuka ritsleting aliran. Jika API Anda tidak mengizinkan saya menulis aliran ke sana dan sebaliknya membuat saya menulis aliran ke suatu file hanya agar saya dapat memasukkannya ke API Anda, maka API Anda rusak otak.
Edward Thomson
52
@EdwardThomson - Baik, jadi buat perpustakaan mendukung file dan stream. Ini buang-buang waktu semua orang - milikku, milikmu, si penanya, dan semua Googler lain yang akan tersandung pada ini bahwa kita masing-masing harus mengimplementasikan Zip Utilities kita sendiri. Sama seperti ada KERING, ada DROP - Jangan Ulangi Orang Lain.
ArtOfWarfare

Jawaban:

291

Saya tahu ini sudah terlambat dan ada banyak jawaban tetapi zip4j ini adalah salah satu perpustakaan terbaik untuk zipping yang saya gunakan. Sederhana (tanpa kode boiler) dan dapat dengan mudah menangani file yang dilindungi kata sandi.

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

Ketergantungan Maven adalah:

<dependency>
    <groupId>net.lingala.zip4j</groupId>
    <artifactId>zip4j</artifactId>
    <version>1.3.2</version>
</dependency>
pengguna2003470
sumber
1
saya mendapat org.zeroturnaround.zip.ZipException: java.io.FileNotFoundException: images \ 001GL.JPG: gagal terbuka: kesalahan EINVAL (Argumen tidak valid)
Smit Patel
4
Apakah ini berfungsi dengan Android?
Ercan
1
Tidak, ini tidak bekerja dengan baik dengan Android, itu tidak mendukung bahasa Cina.
Dhiraj Himani
3
Zip4J tidak mendukung membaca zip dari inputstream, hanya dari disk.
Renaud Cerrato
2
Situs web tampaknya tidak memiliki javadoc.
JohnC
76

Dengan Apache Commons-IO 's IOUtilsAnda dapat melakukan ini:

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

Ini masih beberapa kode boilerplate, tetapi hanya memiliki 1 ketergantungan non-eksotis: Commons-IO

Geoffrey De Smet
sumber
1
@VitalySazanovich yang Anda maksud adalah Java 7 ZipEntry.
Randy
2
Terima kasih. Juga perlu zipFile.close () di akhir.
JoshuaD
4
mengapa tidak IOUtils.closeQuietly (keluar)?
Juan Mendez
2
@JuanMendez karena jika ada kesalahan saat ditutup, Anda tidak dapat memastikan file tersebut disimpan sepenuhnya dan dengan benar. Tapi selain close()itu normal tidak sakit.
vadipp
3
Solusi ini rentan terhadap ZipSlip (zip4j juga terpengaruh )
Marcono1234
40

Ekstrak file zip dan semua subfoldernya, hanya menggunakan 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());
    }

}

File zip dan semua subfoldernya:

 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
sumber
7
Panggilan untuk menutup harus di dalam "akhirnya" setidaknya. Pengecualian tidak ditangani dengan baik. -> Saya kira itulah bagian dari mengapa OP meminta perpustakaan untuk digunakan.
4
Ini terlalu banyak kode. Ini dapat dilakukan dalam 2 baris.
Makky
/mnt/sdcard/final_unzip_data/Product_images\001GL.JPG: gagal terbuka: EINVAL (Argumen tidak valid)
Smit Patel
@ Jo Michael Terima kasih sobat telah memposting ini. Ini memecahkan masalah saya. Saya akan memberi Anda +1 untukextractFolder(String zipFile,String extractFolder)
OO7
Kode ini tidak menyimpan atribut dan izin file ... jika Anda menggunakan sesuatu seperti ini untuk unzip aplikasi runnable, bersiaplah untuk kesalahan aneh mengenai izin file. Ini membuat saya sakit kepala selama seminggu.
Renato
23

Opsi lain yang dapat Anda periksa adalah zt-zip tersedia dari Maven central dan halaman proyek di https://github.com/zeroturnaround/zt-zip

Ini memiliki fungsi pengemasan dan pembongkaran standar (pada aliran dan pada sistem file) + banyak metode pembantu untuk menguji file dalam arsip atau menambah / menghapus entri.

toomasr
sumber
17

Implementasi penuh untuk Zip / Unzip Folder / File dengan zip4j


Unduh toples dari sini , dan tambahkan ke jalur build proyek Anda. Di classbawah ini dapat mengkompres dan mengekstrak file atau folder apa pun, dengan atau tanpa perlindungan kata sandi-

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
sumber
1
Jawaban dan perpustakaan yang bagus. Mengekstraksi 1868 file membutuhkan waktu ~ 15 detik di perpustakaan ini, dibandingkan dengan 20+ menit saat menggunakan ZipInputStream (karena alasan tertentu)
Jonty800
8

Proyek yang sangat bagus adalah TrueZip .

TrueZIP adalah kerangka kerja plug-in berbasis Java untuk sistem file virtual (VFS) yang menyediakan akses transparan ke file arsip seolah-olah itu hanya direktori biasa

Misalnya (dari situs web ):

File file = new TFile("archive.tar.gz/README.TXT");
OutputStream out = new TFileOutputStream(file);
try {
   // Write archive entry contents here.
   ...
} finally {
   out.close();
}
Michael
sumber
Pustaka terlihat bagus - masih belum jelas bagaimana cara unzip file zip yang diberi zipinputstream / file / path.
pathikrit
1
TrueZIP tampaknya tidak menangani pembacaan dengan sangat baik.
Teo Klestrup Röijezon
5
Bukankah sebagian besar sama dengan apa yang dapat Anda lakukan di Java 7? (lihat ZipFileSystemProvider ).
peterh
1
@ Peterh: ZipFileSystemProvider standar-JDK akan menjadi jawaban yang baik. Hanya beberapa orang yang melihatnya sebagai komentar.
iuzuz
3

Pilihan lain adalah JZlib . Dalam pengalaman saya, ini kurang "terpusat pada file" dari zip4J, jadi jika Anda perlu bekerja di gumpalan di-memori daripada file, Anda mungkin ingin melihatnya.

Henrik Aasted Sørensen
sumber
0

Apakah Anda sudah melihat http://commons.apache.org/vfs/ ? Ia mengklaim untuk menyederhanakan banyak hal untuk Anda. Tapi saya tidak pernah menggunakannya dalam proyek.

Saya juga tidak mengetahui lib kompresi Java-Native selain JDK atau Apache Compression.

Saya ingat sekali kita merobek beberapa fitur dari Apache Ant - mereka memiliki banyak utilitas untuk kompresi / dekompresi bawaan.

Kode sampel dengan VFS akan terlihat seperti:

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
sumber
1
Sepertinya dukungan untuk file zip dalam hal-hal VFS sendiri sangat terbatas: commons.apache.org/vfs/filesystems.html
TJ Crowder