我查看了JDK和Apache压缩库附带的默认Zip库,我对它们不满意,原因有3个:

They are bloated and have bad API design. I have to write 50 lines of boiler plate byte array output, zip input, file out streams and close relevant streams and catch exceptions and move byte buffers on my own? Why can't I have a simple API that looks like this Zipper.unzip(InputStream zipFile, File targetDirectory, String password = null) and Zipper.zip(File targetDirectory, String password = null) that just works? It seems zipping unzipping destroys file meta-data and password handling is broken. Also, all the libraries I tried were 2-3x slow compared to the command line zip tools I get with UNIX?

对我来说(2)和(3)是次要的点,但我真的想要一个良好的测试库与一行接口。


你看了http://commons.apache.org/vfs/吗?它声称可以为你简化很多事情。但我从未在项目中使用过。

除了JDK或Apache压缩之外,我也不知道Java-Native压缩库。

我记得有一次我们从Apache Ant中剥离了一些特性——它们有很多内置的压缩/解压utils。

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

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

}

Zip文件及其所有子文件夹:

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

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

在Java 8中,使用Apache Commons-IO的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);
        }
    }
  }
}

它仍然是一些样板代码,但它只有一个非外来依赖项:Commons-IO

在Java 11及更高版本中,可能会有更好的选择,请参阅ZhekaKozlov的评论。


我知道有点晚了,有很多答案,但这个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>

另一个选项是zt-zip,可从Maven中心和项目页面https://github.com/zeroturnaround/zt-zip获得

它具有标准的打包和解包功能(在流和文件系统上)+许多辅助方法来测试存档中的文件或添加/删除条目。


另一个选择是JZlib。根据我的经验,它不像zip4J那样“以文件为中心”,所以如果您需要处理内存中的blobs而不是文件,那么您可能需要考虑一下它。


这里有一个递归压缩和解压缩文件的完整示例: http://developer-tips.hubpages.com/hub/Zipping-and-Unzipping-Nested-Directories-in-Java-using-Apache-Commons-Compress


使用zip4j完全实现压缩/解压缩文件夹/文件


将此依赖项添加到构建管理器中。或者,从这里下载最新的JAR文件,并将其添加到项目构建路径中。类bellow可以压缩和解压缩任何文件或文件夹有或没有密码保护-

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);
            } else {
                //neither file nor directory
            }

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

更详细的使用方法请参见这里。