什么是压缩/解压缩文件的好 Java 库? [关闭]

Posted

技术标签:

【中文标题】什么是压缩/解压缩文件的好 Java 库? [关闭]【英文标题】:What is a good Java library to zip/unzip files? [closed] 【发布时间】:2012-03-08 15:46:06 【问题描述】:

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

    它们臃肿且 API 设计不佳。我必须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?为什么我不能有一个看起来像 Zipper.unzip(InputStream zipFile, File targetDirectory, String password = null)Zipper.zip(File targetDirectory, String password = null) 这样的简单 API 可以正常工作?

    似乎压缩解压缩会破坏文件元数据并且密码处理被破坏。

    此外,与我在 UNIX 中获得的命令行 zip 工具相比,我尝试的所有库都慢了 2-3 倍?

对我来说 (2) 和 (3) 是次要的,但我真的想要一个经过良好测试的具有单行界面的库。

【问题讨论】:

至于#1,这是因为不是每个人都只是将文件解压缩到目录中。如果您总是使用相同的模式,为什么不编写一个实用类来包装其他模式并执行您需要的操作并使用 that @EdwardThomson 因为使用库比编写代码、测试代码和维护代码更容易。 @EdwardThomson:您的论点无效。查看 Python zip API:docs.python.org/3/library/zipfile。您需要 1 行代码来压缩或解压缩文件。 API 应该很好地处理常见情况,除了压缩或解压缩之外,我想不出任何 zip API 的用例。 @wrick:压缩文件或解压缩文件是压缩或解压缩流的一种特殊情况。如果您的 API 不允许我向其写入流,而是让我将流写入文件,以便我可以将其提供给您的 API,那么您的 API 是脑残了。 @EdwardThomson - 很好,所以让库同时支持文件和流。这是浪费每个人的时间——我的、你的、提问者以及所有其他会偶然发现我们每个人都必须实现自己的 Zip 实用程序的 Google 员工。就像有 DRY 一样,也有 DROP - 不要重复其他人。 【参考方案1】:

我知道它很晚,并且有很多答案,但是这个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>

【讨论】:

我得到了 org.zeroturnaround.zip.ZipException: java.io.FileNotFoundException: images\001GL.JPG: open failed: EINVAL (Invalid argument) 错误 它适用于 android 吗? 不,它不能在android上正常工作,它不支持中文。 Zip4J 不支持从输入流中读取 zip,只能从磁盘中读取。 网站似乎没有 javadoc。【参考方案2】:

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

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

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

【讨论】:

谢谢。最后还需要 zipFile.close()。 为什么不 IOUtils.closeQuietly(out)? @JuanMendez 因为如果关闭时出现错误,您无法确定文件是否完全正确保存。但除了普通的close() 之外,它不会受到伤害。 此方案易受ZipSlip的攻击(zip4j也是affected) 在 Java 9+ 中,您不再需要 IOUtils。只需写zipFile.getInputStream(entry).transferTo(outputStream)【参考方案3】:

仅使用 JDK 提取 zip 文件及其所有子文件夹:

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

【讨论】:

关闭的调用至少应该在“finally”块内。异常处理不好。 -> 我想这就是 OP 要求使用 library 的部分原因。 代码太多了。这可以在 2 行中完成。 /mnt/sdcard/final_unzip_data/Product_images\001GL.JPG:打开失败:EINVAL(无效参数) @Joe Michael 感谢好友发布此消息。它解决了我的问题。我会给你 +1 extractFolder(String zipFile,String extractFolder) 此代码不保留文件属性和权限...如果您使用类似的方法解压缩可运行的应用程序,请为文件权限方面的奇怪错误做好准备。这让我头痛了一个星期。【参考方案4】:

您可以查看的另一个选项是 zt-zip,可从 Maven 中心和项目页面https://github.com/zeroturnaround/zt-zip

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

【讨论】:

【参考方案5】:

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


将this dependency 添加到您的构建管理器。或者,将最新的 JAR 文件从 here 和 add it 下载到您的项目构建路径。 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);
             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);
    /**/

更详细的使用方法请见here。

【讨论】:

一个不错的答案和库。在这个库上提取 1868 个文件大约需要 15 秒,而使用 ZipInputStream 时需要 20 多分钟(出于某种原因) @Jonty800 有了这样的性能差异,您也许应该再看看您的实现。如果您不缓冲流并且每个字节都直接从设备读取/写入,那么您将获得这样的性能差异。我刚刚提取了 17588 个文件,总大小为 1.8 GB,zip4j 耗时 64 秒,而缓冲标准库实现耗时 39 秒。话虽如此,一个简单的 BufferedOutputStream 实现大约需要 5 分钟。【参考方案6】:

一个非常好的项目是TrueZip。

TrueZIP 是一个基于 Java 的虚拟文件系统 (VFS) 插件框架,它提供对归档文件的透明访问,就好像它们只是普通目录一样

例如(来自website):

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

【讨论】:

这个库看起来不错 - 如何在给定 zipinputstream/file/path 的情况下简单地解压缩 zip 文件仍然不明显。 TrueZIP 似乎不能很好地处理从流中读取。 这不是和你在 Java 7 中可以做的一样吗? (看ZipFileSystemProvider)。 @peterh:标准 JDK ZipFileSystemProvider 将是一个很好的答案。只有少数人将其视为评论。【参考方案7】:

另一个选项是JZlib。根据我的经验,它不像 zip4J 那样“以文件为中心”,因此如果您需要处理内存中的 blob 而不是文件,您可能需要查看一下。

【讨论】:

【参考方案8】:

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

【讨论】:

【参考方案9】:

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

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

我记得有一次我们从 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();

【讨论】:

看起来对 VFS 文件中 zip 文件的支持非常有限:commons.apache.org/vfs/filesystems.html

以上是关于什么是压缩/解压缩文件的好 Java 库? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript字符串压缩+ PHP解压缩[关闭]

Java压缩与解压缩问题

在提交到存储库之前解压缩压缩的数据文件

如何在 Java 中压缩 JSON 并在 Javascript 中解压缩 [关闭]

如何用java进行多线程解压缩大文件夹 - 首选java8?

解压缩/解压缩 JavaScript 的工具 [关闭]