Một thư viện Java tốt để nén / giải nén các tệp là gì? [đóng cửa]


232

Tôi đã xem thư viện Zip mặc định đi kèm với các lib nén JDK và Apache và tôi không hài lòng với chúng vì 3 lý do:

  1. Chúng cồng kềnh và có thiết kế API xấu. Tôi phải viết 50 dòng đầu ra mảng byte của nồi hơi, đầu vào zip, luồng ra và đóng các luồng có liên quan và bắt ngoại lệ và tự di chuyển bộ đệm byte ? Tại sao tôi không thể có một API đơn giản trông như thế này Zipper.unzip(InputStream zipFile, File targetDirectory, String password = null)Zipper.zip(File targetDirectory, String password = null)nó chỉ hoạt động?

  2. Có vẻ như việc nén unzipping phá hủy dữ liệu meta tập tin và xử lý mật khẩu bị hỏng.

  3. Ngoài ra, tất cả các thư viện tôi đã thử đều chậm 2-3 lần so với các công cụ zip dòng lệnh tôi có với UNIX?

Đối với tôi (2) và (3) là những điểm nhỏ nhưng tôi thực sự muốn có một thư viện được thử nghiệm tốt với giao diện một dòng.


13
Đối với # 1, đó là bởi vì không phải ai cũng chỉ đơn giản là giải nén một tệp vào một thư mục. Nếu bạn luôn sử dụng cùng một mẫu, tại sao không viết một lớp tiện ích bao bọc một trong những mẫu khác và thực hiện những gì bạn cần nó và chỉ sử dụng ?
Edward Thomson

14
@EdwardThomson vì sử dụng thư viện dễ dàng hơn viết mã, kiểm tra mã và duy trì mã.
Zak

11
@EdwardThomson: Đối số của bạn không hợp lệ. Hãy xem API zip của Python: docs.python.org/3/l Library / zipfile . Bạn cần 1 dòng mã để nén hoặc giải nén tập tin. API nên xử lý trường hợp phổ biến rất tốt và tôi không thể nghĩ ra bất kỳ trường hợp sử dụng nào của API zip ngoài việc nén hoặc giải nén.
pathikrit

7
@wrick: nén tệp hoặc giải nén tệp là trường hợp đặc biệt của nén hoặc giải nén luồng. Nếu API của bạn không cho phép tôi viết một luồng cho nó và thay vào đó bắt tôi viết một luồng vào một tệp để tôi có thể cung cấp luồng đó cho API của bạn, thì API của bạn bị hỏng não.
Edward Thomson

52
@EdwardThomson - Tốt thôi, vì vậy hãy tạo thư viện hỗ trợ cả tệp và luồng. Thật lãng phí thời gian của mọi người - của tôi, của bạn, của người hỏi và tất cả các nhân viên Google khác, những người sẽ vấp phải điều này mà mỗi chúng ta phải thực hiện Tiện ích Zip của riêng mình. Giống như có DRY, có DROP - Không lặp lại người khác.
ArtOfWarfare

Câu trả lời:


291

Tôi biết nó muộn và có rất nhiều câu trả lời nhưng zip4j này là một trong những thư viện tốt nhất để nén mà tôi đã sử dụng. Nó đơn giản (không có mã nồi hơi) và có thể dễ dàng xử lý các tệp được bảo vệ bằng mật khẩu.

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

Phụ thuộc Maven là:

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

1
tôi đã nhận org.zeroturnaround.zip.ZipException: java.io.FileNotFoundException: images \ 001GL.JPG: mở thất bại: EINVAL (lập luận không hợp lệ) lỗi
Smit Patel

4
Nó có hoạt động với Android không?
Ercan

1
Không có nó không hoạt động tốt với Android, Nó không hỗ trợ ngôn ngữ Trung Quốc.
Dhiraj Himani

3
Zip4J không hỗ trợ đọc zip từ đầu vào, chỉ từ đĩa.
Renaud Cerrato

2
Trang web dường như không có javadoc.
JohnC

76

Với Apache Commons-IO , IOUtilsbạn có thể làm điều này:

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

Đây vẫn là một số mã soạn sẵn, nhưng nó chỉ có 1 phụ thuộc không kỳ lạ: Commons-IO


1
@VitalySazanovich bạn đang đề cập đến ZipEntry Java 7.
Randy

2
Cảm ơn. Cũng cần zipFile.close () ở cuối.
JoshuaD

4
Tại sao không IOUtils.closeQuietly (ra)?
Juan Mendez

2
@JuanMendez vì nếu có lỗi khi đóng, bạn không thể chắc chắn tệp đã được lưu hoàn toàn và chính xác. Nhưng ngoài ra bình thường close()nó sẽ không đau.
vadipp

3
Giải pháp này dễ bị ZipSlip (zip4j cũng bị ảnh hưởng )
Marcono1234

40

Trích xuất tệp zip và tất cả các thư mục con của nó, chỉ sử dụng 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());
    }

}

Tệp zip và tất cả các thư mục con của nó:

 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
Các cuộc gọi để đóng ít nhất phải ở trong các khối "cuối cùng". Các ngoại lệ không được xử lý tốt. -> Tôi đoán đó là một phần lý do tại sao OP yêu cầu thư viện sử dụng.

4
Đây là quá nhiều mã. Điều này có thể được thực hiện trong 2 dòng.
Makky

/mnt/sdcard/final_unzip_data/ Productt_images\001GL.JPG: mở không thành công: EINVAL (Đối số không hợp lệ)
Smit Patel

@Joe Michael Cảm ơn bạn thân đã đăng bài này. Nó giải quyết vấn đề của tôi. Tôi sẽ cho bạn +1 choextractFolder(String zipFile,String extractFolder)
OO7

Mã này không giữ các thuộc tính và quyền của tệp ... nếu bạn sử dụng một cái gì đó như thế này để giải nén một ứng dụng có thể chạy được, hãy chuẩn bị cho các lỗi lạ liên quan đến quyền của tệp. Điều này đã khiến tôi mất một tuần đau đầu.
Renato

23

Một tùy chọn khác mà bạn có thể kiểm tra là zt-zip có sẵn từ trung tâm Maven và trang dự án tại https://github.com/zeroturnaround/zt-zip

Nó có chức năng đóng gói và giải nén tiêu chuẩn (trên luồng và trên hệ thống tệp) + rất nhiều phương thức trợ giúp để kiểm tra các tệp trong kho lưu trữ hoặc thêm / xóa các mục.


17

Thực hiện đầy đủ để Zip / Giải nén thư mục / Tệp bằng zip4j


Tải về jar từ đâythêm nó vào đường dẫn xây dựng dự án của bạn. Dưới đây classcó thể nén và trích xuất bất kỳ tập tin hoặc thư mục, có hoặc không có mật khẩu bảo vệ-

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
Một câu trả lời tốt đẹp và thư viện. Trích xuất 1868 tệp mất ~ 15 giây trên thư viện này, so với hơn 20 phút khi sử dụng ZipInputStream (vì một số lý do)
Jonty800

8

Một dự án rất hay là TrueZip .

TrueZIP là một khung trình cắm thêm dựa trên Java cho các hệ thống tệp ảo (VFS) cung cấp quyền truy cập minh bạch vào các tệp lưu trữ như thể chúng chỉ là các thư mục đơn giản

Ví dụ: (từ trang web ):

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

Thư viện trông rất đẹp - vẫn chưa rõ ràng làm thế nào để giải nén một tệp zip được cung cấp một zipinputstream / file / path.
pathikrit

1
TrueZIP dường như không xử lý việc đọc từ các luồng rất tốt.
Teo Klestrup RöijIDIA

5
Nó không giống với những gì bạn có thể làm trong Java 7 phải không? (nhìn vào ZipFileSystemProvider ).
peterh

1
@peterh: ZipFileSystemProvider tiêu chuẩn-JDK sẽ là một câu trả lời tốt. Chỉ có vài người xem nó là bình luận.
iuzuz

3

Một lựa chọn khác là JZlib . Theo kinh nghiệm của tôi, nó ít "tập trung vào tập tin" hơn zip4J, vì vậy nếu bạn cần làm việc trên các đốm trong bộ nhớ hơn là các tệp, bạn có thể muốn xem qua nó.



0

Bạn đã xem http://commons.apache.org/vfs/ chưa? Nó tuyên bố để đơn giản hóa rất nhiều thứ cho bạn. Nhưng tôi chưa bao giờ sử dụng nó trong một dự án.

Tôi cũng không nhận thức được các lib nén Java-Native khác với JDK hoặc Apache Nén.

Tôi nhớ một khi chúng tôi tách một số tính năng ra khỏi Apache Ant - chúng có rất nhiều tiện ích để nén / giải nén được tích hợp.

Mã mẫu với VFS sẽ trông như sau:

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
Có vẻ như sự hỗ trợ cho các tệp zip trong công cụ VFS khá hạn chế: commons.apache.org/vfs/filesystems.html
TJ Crowder
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.