อะไรคือห้องสมุด Java ที่ดีสำหรับการ zip / unzip ไฟล์? [ปิด]


232

ฉันดูไลบรารี Zip เริ่มต้นที่มาพร้อมกับ JDK และ Apache อัด libs และฉันไม่พอใจพวกเขาด้วยเหตุผล 3 ประการ:

  1. พวกเขามีป่องและมีการออกแบบ API ที่ไม่ดี ฉันต้องเขียนแถวหม้อไอน้ำจำนวน 50 บรรทัด, ใส่รหัสไปรษณีย์, ใส่ไฟล์สตรีมและปิดสตรีมที่เกี่ยวข้องและจับข้อยกเว้นและย้ายบัฟเฟอร์ไบท์ด้วยตัวเอง ? เหตุใดฉันจึงไม่มี API แบบง่ายที่มีลักษณะเช่นนี้Zipper.unzip(InputStream zipFile, File targetDirectory, String password = null)และZipper.zip(File targetDirectory, String password = null)ใช้ได้

  2. ดูเหมือนว่าการซิปการคลายซิปจะเป็นการทำลายเมตาดาต้าไฟล์และการจัดการรหัสผ่านจะใช้งานไม่ได้

  3. นอกจากนี้ไลบรารีทั้งหมดที่ฉันลองใช้นั้นช้ากว่า 2-3 เท่าเมื่อเปรียบเทียบกับเครื่องมือ zip ของบรรทัดคำสั่งที่ฉันใช้กับ UNIX

สำหรับฉัน (2) และ (3) เป็นคะแนนย่อย แต่ฉันต้องการห้องสมุดทดสอบที่ดีพร้อมอินเทอร์เฟซบรรทัดเดียว


13
สำหรับ # 1 อาจเป็นเพราะทุกคนไม่ใช่เพียงการขยายไฟล์ไปยังไดเรกทอรี หากคุณใช้รูปแบบเดียวกันอยู่เสมอทำไมไม่เพียงแค่เขียนคลาสยูทิลิตี้ที่ล้อมรอบหนึ่งในสิ่งอื่น ๆ และทำสิ่งที่คุณต้องการและใช้มัน ?
Edward Thomson

14
@EdwardThomson เพราะใช้ไลบรารีได้ง่ายกว่าเขียนโค้ดทดสอบและบำรุงรักษาโค้ด
แซค

11
@EdwardThomson: อาร์กิวเมนต์ของคุณไม่ถูกต้อง ดูที่ซิป API หลาม: docs.python.org/3/library/zipfile คุณต้องมีรหัส 1 บรรทัดในการซิปหรือคลายซิปไฟล์ API ควรจัดการกรณีทั่วไปได้เป็นอย่างดีและฉันไม่สามารถนึกถึงกรณีการใช้งานใด ๆ ของ zip API นอกเหนือจากการซิปหรือการคลายซิป
pathikrit

7
@wrick: การซิปไฟล์หรือคลายซิปไฟล์เป็นกรณีพิเศษของการซิปหรือคลายซิปสตรีม หาก API ของคุณไม่ให้ฉันเขียนสตรีมลงไปและทำให้ฉันเขียนสตรีมไปยังไฟล์แทนเพื่อที่ฉันจะสามารถป้อนฟีดนั้นไปยัง API ของคุณ API ของคุณจะเสียหาย
เอ็ดเวิร์ดทอมสัน

52
@EdwardThomson - ดีดังนั้นทำให้ไลบรารีรองรับทั้งไฟล์และสตรีม เป็นการเสียเวลาของทุกคนไม่ว่าจะเป็นของคุณผู้ถามและชาว Googler คนอื่น ๆ ที่จะสะดุดกับเรื่องนี้ที่เราแต่ละคนต้องใช้ Zip Utilities ของเราเอง เช่นเดียวกับที่มีแห้งมี DROP - อย่าทำซ้ำคนอื่น
ArtOfWarfare

คำตอบ:


291

ฉันรู้ว่ามันสายและมีคำตอบมากมาย แต่zip4jนี้เป็นหนึ่งในห้องสมุดที่ดีที่สุดสำหรับการซิปฉันใช้ มันง่าย (ไม่มีรหัสบอยเลอร์) และสามารถจัดการไฟล์ที่ป้องกันด้วยรหัสผ่านได้อย่างง่ายดาย

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

การพึ่งพา Maven คือ:

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

1
ฉันได้ org.zeroturnaround.zip.ZipException: java.io.FileNotFoundException: images \ 001GL.JPG: เปิดล้มเหลว: ข้อผิดพลาด EINVAL (อาร์กิวเมนต์ไม่ถูกต้อง)
Smit Patel

4
ใช้งานกับ Android ได้หรือไม่
Ercan

1
มันไม่ทำงานได้ดีกับ android มันไม่รองรับภาษาจีน
Dhiraj Himani

3
Zip4J ไม่รองรับการอ่าน zip จากอินพุตสตรีมจากดิสก์เท่านั้น
Renaud Cerrato

2
เว็บไซต์ดูเหมือนจะไม่มี javadoc
JohnC

76

ด้วยApache Commons-IO 's IOUtilsคุณสามารถทำสิ่งนี้:

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

ยังคงมีรหัสสำเร็จรูปบางส่วน แต่มีการพึ่งพาที่ไม่แปลกใหม่เพียง 1 รายการ: Commons-IO


1
@VitalySazanovich คุณกำลังอ้างถึง Java 7 ZipEntry
แรนดี้

2
ขอบคุณ ต้องการ zipFile.close () ที่ส่วนท้ายด้วย
JoshuaD

4
ทำไมไม่ IOUtils ปิดเงียบ ๆ (ออก)
Juan Mendez

2
@JuanMendez เพราะหากมีข้อผิดพลาดในการปิดคุณไม่สามารถมั่นใจได้ว่าไฟล์ถูกบันทึกอย่างสมบูรณ์และถูกต้อง แต่นอกเหนือไปจากปกติclose()มันจะไม่เจ็บ
vadipp

3
วิธีนี้มีความเสี่ยงต่อZipSlip (zip4j ได้รับผลกระทบเช่นกัน)
Marcono1234

40

แตกไฟล์ zip และโฟลเดอร์ย่อยทั้งหมดโดยใช้เฉพาะ 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());
    }

}

ไฟล์ซิปและโฟลเดอร์ย่อยทั้งหมด:

 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
การโทรเพื่อปิดควรอยู่ในบล็อค "ในที่สุด" อย่างน้อยที่สุด ข้อยกเว้นไม่ได้รับการจัดการที่ดี -> ฉันเดาว่าเป็นส่วนหนึ่งของสาเหตุที่ OP ขอให้ห้องสมุดใช้

4
นี่เป็นรหัสที่มากเกินไป สามารถทำได้ 2 บรรทัด
Makky

/mnt/sdcard/final_unzip_data/Product_images\001GL.JPG: การเปิดล้มเหลว: EINVAL (อาร์กิวเมนต์ไม่ถูกต้อง)
Smit Patel

@Joe Michael ขอบคุณเพื่อนที่โพสต์สิ่งนี้ มันแก้ปัญหาของฉัน ฉันจะให้ +1 ของคุณสำหรับextractFolder(String zipFile,String extractFolder)
OO7

รหัสนี้ไม่ได้เก็บคุณสมบัติไฟล์และการอนุญาต ... หากคุณใช้บางอย่างเช่นนี้ในการคลายซิปแอปพลิเคชั่นที่รันได้ให้เตรียมไว้สำหรับข้อผิดพลาดแปลก ๆ เกี่ยวกับการอนุญาตไฟล์ สิ่งนี้ทำให้ฉันปวดหัวหนึ่งสัปดาห์
Renato

23

ตัวเลือกอื่นที่คุณสามารถตรวจสอบได้คือzt-zipจาก Maven central และหน้าโครงการที่https://github.com/zeroturnaround/zt-zip

มันมีฟังก์ชั่นการบรรจุและการเอาออกมาตรฐาน (บนสตรีมและบนระบบไฟล์) + วิธีการช่วยเหลือมากมายเพื่อทดสอบไฟล์ในไฟล์เก็บถาวรหรือเพิ่ม / ลบรายการ


17

การติดตั้ง Zip / Unzip อย่างเต็มรูปแบบให้กับโฟลเดอร์ / ไฟล์ด้วยzip4j


ดาวน์โหลด jar จากที่นี่และเพิ่มไปยังเส้นทางการสร้างโครงการของคุณ ซอลเบลclassโลว์สามารถบีบอัดและแตกไฟล์หรือโฟลเดอร์ใดก็ได้โดยมีหรือไม่มีการป้องกันด้วยรหัสผ่าน

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
คำตอบที่ดีและห้องสมุด การแยกไฟล์ 1868 ใช้เวลา ~ 15 วินาทีในห้องสมุดนี้เมื่อเทียบกับ 20+ นาทีเมื่อใช้ ZipInputStream (ด้วยเหตุผลบางอย่าง)
309 Jonty800

8

โครงการที่ดีมากคือTrueZip

TrueZIP เป็นเฟรมเวิร์กปลั๊กอินที่ใช้ Java สำหรับระบบไฟล์เสมือน (VFS) ซึ่งให้การเข้าถึงไฟล์เก็บถาวรที่โปร่งใสราวกับว่ามันเป็นเพียงไดเรกทอรีธรรมดา

ตัวอย่างเช่น (จากเว็บไซต์ ):

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

ห้องสมุดดูดี - ยังไม่ชัดเจนว่าจะแตกซิปเพียงไฟล์ zip อย่างไรโดยให้ zipinput สตรีม / ไฟล์ / พา ธ
pathikrit

1
TrueZIP ดูเหมือนจะจัดการกับการอ่านจากสตรีมได้ดีมาก
Teo Klestrup Röijezon

5
มันไม่เหมือนกับสิ่งที่คุณสามารถทำได้ใน Java 7 หรือไม่ (ดูที่ZipFileSystemProvider )
peterh

1
@ peterh: มาตรฐาน -JKK ZipFileSystemProvider จะเป็นคำตอบที่ดี มีเพียงไม่กี่คนเท่านั้นที่เห็นว่าเป็นความคิดเห็น
iuzuz

3

ตัวเลือกหนึ่งคือJZlib จากประสบการณ์ของฉันมันน้อยกว่า "file-centered" มากกว่า zip4J ดังนั้นถ้าคุณต้องการทำงานใน blobs ในหน่วยความจำแทนที่จะเป็นไฟล์คุณอาจต้องการดูมัน



0

คุณดูhttp://commons.apache.org/vfs/หรือยัง มันอ้างว่าทำสิ่งต่าง ๆ ให้คุณง่ายขึ้น แต่ฉันไม่เคยใช้มันในโครงการ

ฉันยังไม่ทราบถึงการบีบอัด Java-Native libs นอกเหนือจาก JDK หรือ Apache Compression

ฉันจำได้ว่าเมื่อเราดึงคุณสมบัติบางอย่างออกมาจาก Apache Ant - พวกมันมีประโยชน์มากมายสำหรับการบีบอัด / คลายการบีบอัดในตัว

โค้ดตัวอย่างที่มี VFS จะมีลักษณะดังนี้:

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
ดูเหมือนว่าการรองรับไฟล์ zip ใน VFS stuff นั้นค่อนข้าง จำกัด : commons.apache.org/vfs/filesystems.html
TJ Crowder
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.