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


232

我查看了JDK和Apache压缩库随附的默认Zip库,由于以下三个原因,我对它们不满意:

  1. 它们are肿并且API设计错误。我必须编写50行样板字节数组输出,zip输入,文件输出流并关闭相关流,并捕获异常并自行移动字节缓冲区?为什么我不能有一个看起来像这样Zipper.unzip(InputStream zipFile, File targetDirectory, String password = null)并且可以正常使用的简单API Zipper.zip(File targetDirectory, String password = null)

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

  3. 另外,与我从UNIX获得的命令行zip工具相比,我尝试的所有库的速度都慢了2-3倍?

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


13
对于#1,这是因为并非每个人都只是将文件解压缩到目录中。如果您始终使用相同的模式,那为什么不编写一个实用程序类,该实用程序类包装其他模式中的一个,并满足您的需要,然后使用呢?
爱德华·汤姆森

14
@EdwardThomson,因为使用库比编写代码,测试代码和维护代码更容易。
Zak

11
@EdwardThomson:您的论点无效。查看Python zip API:docs.python.org/3/library/zipfile。您需要1行代码来压缩或解压缩文件。API应该很好地处理常见情况,除了压缩或解压缩之外,我无法想到其他任何zip API用例。
pathikrit

7
@wrick:压缩文件或解压缩文件是压缩或解压缩流的一种特殊情况。如果您的API不允许我向其写入流,而是让我仅将流写入文件以便可以将其提供给您的API,则您的API受到了脑部损害。
爱德华·汤姆森

52
@EdwardThomson-很好,因此使该库支持文件和流。这浪费了每个人的时间-我的,您的,请求者和所有其他Google员工都会发现,我们每个人都必须实施自己的Zip实用程序。就像有干一样,还有DROP-不要重复别人。
ArtOfWarfare 2013年

Answers:


291

我知道它晚了,有很多答案,但是这个zip4j是我使用过的最好的zip库之一。它简单(没有锅炉代码),并且可以轻松处理受密码保护的文件。

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 2015年

1
没有它与android不能正常工作,它不支持中文。
Dhiraj Himani

3
Zip4J不支持从输入流中读取zip,而仅从磁盘中读取。
雷诺·塞拉托

2
网站似乎没有javadoc。
JohnC

76

使用Apache Commons-IOIOUtils您可以执行以下操作:

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。
兰迪2014年

2
谢谢。最后还需要zipFile.close()。
2015年

4
为什么不IOUtils.closeQuietly(out)?
胡安·门德斯

2
@JuanMendez,因为如果关闭时出错,则无法确定文件是否完全正确地保存。但是除了正常close()情况外,它不会受到伤害。
vadipp

3
此解决方案容易受到ZipSlip的攻击(zip4j也受到影响
Marcono1234 '19

40

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

7
关闭调用至少应在“ finally”块内。异常处理得不好。->我想这就是OP要求使用的部分原因。

4
这是太多的代码。这可以分两行完成。
Makky

/mnt/sdcard/final_unzip_data/Product_images\001GL.JPG:打开失败:EINVAL(无效参数)
Smit Patel

@Joe Michael感谢好友发布了此内容。它解决了我的问题。我会给您+1extractFolder(String zipFile,String extractFolder)
OO7年4

这段代码不会保留文件属性和权限...如果您使用类似的方法来解压缩可运行的应用程序,请做好有关文件权限的奇怪错误的准备。这让我头疼了一周。
雷纳托

23

您可以检出的另一个选项是Mt中央和项目页面的https://github.com/zeroturnaround/zt-zip提供的zt-zip

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


17

使用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秒,而使用ZipInputStream则花费了20多分钟(出于某种原因)
Jonty800,18年

8

一个非常不错的项目是TrueZip

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

例如(从网站):

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文件解压缩仍然不明显。
pathikrit 2012年

1
TrueZIP似乎不能很好地处理流中的读取。
Teo KlestrupRöijezon,13年

5
它在很大程度上与Java 7中的功能相同吗?(查看ZipFileSystemProvider)。
彼得2014年

1
@peterh:标准的JDK ZipFileSystemProvider将是一个很好的答案。只有很少的人将其视为评论。
iuzuz



0

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

除了JDK或Apache压缩,我还不了解Java本地压缩库。

我记得曾经从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
看起来VFS本身对zip文件的支持非常有限:commons.apache.org/vfs/filesystems.html
TJ Crowder
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.