Zipping and Unzipping in Java – Java中的压缩和解压

最后修改: 2016年 4月 4日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this quick tutorial, we’ll learn how to zip a file into an archive and how to unzip the archive, all using core libraries provided by Java.

在这个快速教程中,我们将学习如何将一个文件压缩成一个档案,以及如何解压缩档案,所有这些都使用Java提供的核心库。

These core libraries are part of the java.util.zip package, where we can find all zipping and unzipping related utilities.

这些核心库是java.util.zip包的一部分,在这里我们可以找到所有与压缩和解压有关的实用程序。

2. Zip a File

2.压缩一个文件

First, let’s have a look at a simple operation, zipping a single file.

首先,让我们看一下一个简单的操作,即压缩一个文件。

For our example, we’ll zip a file named test1.txt into an archive named compressed.zip.

在我们的例子中,我们将把一个名为test1.txt的文件压缩到一个名为compressed.zip的存档中。

Of course, we’ll first access the file from a disk:

当然,我们首先要从磁盘上访问该文件。

public class ZipFile {
    public static void main(String[] args) throws IOException {
        String sourceFile = "test1.txt";
        FileOutputStream fos = new FileOutputStream("compressed.zip");
        ZipOutputStream zipOut = new ZipOutputStream(fos);
        File fileToZip = new File(sourceFile);
        FileInputStream fis = new FileInputStream(fileToZip);
        ZipEntry zipEntry = new ZipEntry(fileToZip.getName());
        zipOut.putNextEntry(zipEntry);
        byte[] bytes = new byte[1024];
        int length;
        while((length = fis.read(bytes)) >= 0) {
            zipOut.write(bytes, 0, length);
        }
        zipOut.close();
        fis.close();
        fos.close();
    }
}

3. Zip Multiple Files

3.压缩多个文件

Next, let’s see how to zip multiple files into one zip file. We’ll compress test1.txt and test2.txt into multiCompressed.zip:

接下来,让我们看看如何将多个文件压缩成一个压缩文件。我们将把test1.txttest2.txt压缩成multiCompressed.zip

public class ZipMultipleFiles {
    public static void main(String[] args) throws IOException {
        List<String> srcFiles = Arrays.asList("test1.txt", "test2.txt");
        FileOutputStream fos = new FileOutputStream("multiCompressed.zip");
        ZipOutputStream zipOut = new ZipOutputStream(fos);
        for (String srcFile : srcFiles) {
            File fileToZip = new File(srcFile);
            FileInputStream fis = new FileInputStream(fileToZip);
            ZipEntry zipEntry = new ZipEntry(fileToZip.getName());
            zipOut.putNextEntry(zipEntry);

            byte[] bytes = new byte[1024];
            int length;
            while((length = fis.read(bytes)) >= 0) {
                zipOut.write(bytes, 0, length);
            }
            fis.close();
        }
        zipOut.close();
        fos.close();
    }
}

4. Zip a Directory

4.压缩一个目录

Now let’s discuss how to zip an entire directory. We’ll compress zipTest into dirCompressed.zip :

现在让我们来讨论如何压缩整个目录。我们将把zipTest压缩到dirCompressed.zip

public class ZipDirectory {
    public static void main(String[] args) throws IOException {
        String sourceFile = "zipTest";
        FileOutputStream fos = new FileOutputStream("dirCompressed.zip");
        ZipOutputStream zipOut = new ZipOutputStream(fos);
        File fileToZip = new File(sourceFile);

        zipFile(fileToZip, fileToZip.getName(), zipOut);
        zipOut.close();
        fos.close();
    }

    private static void zipFile(File fileToZip, String fileName, ZipOutputStream zipOut) throws IOException {
        if (fileToZip.isHidden()) {
            return;
        }
        if (fileToZip.isDirectory()) {
            if (fileName.endsWith("/")) {
                zipOut.putNextEntry(new ZipEntry(fileName));
                zipOut.closeEntry();
            } else {
                zipOut.putNextEntry(new ZipEntry(fileName + "/"));
                zipOut.closeEntry();
            }
            File[] children = fileToZip.listFiles();
            for (File childFile : children) {
                zipFile(childFile, fileName + "/" + childFile.getName(), zipOut);
            }
            return;
        }
        FileInputStream fis = new FileInputStream(fileToZip);
        ZipEntry zipEntry = new ZipEntry(fileName);
        zipOut.putNextEntry(zipEntry);
        byte[] bytes = new byte[1024];
        int length;
        while ((length = fis.read(bytes)) >= 0) {
            zipOut.write(bytes, 0, length);
        }
        fis.close();
    }
}

Note that:

请注意,。

  • To zip sub-directories, we iterate through them recursively.
  • Every time we find a directory, we append its name to the descendants ZipEntry name to save the hierarchy.
  • We also create a directory entry for every empty directory.

5. Unzip an Archive

5.解压缩档案

Now let’s unzip an archive and extract its contents.

现在让我们来解压缩一个档案并提取其内容。

For this example, we’ll unzip compressed.zip into a new folder named unzipTest:

在这个例子中,我们将解压compressed.zip到一个名为unzipTest:的新文件夹。

public class UnzipFile {
    public static void main(String[] args) throws IOException {
        String fileZip = "src/main/resources/unzipTest/compressed.zip";
        File destDir = new File("src/main/resources/unzipTest");
        byte[] buffer = new byte[1024];
        ZipInputStream zis = new ZipInputStream(new FileInputStream(fileZip));
        ZipEntry zipEntry = zis.getNextEntry();
        while (zipEntry != null) {
           // ...
        }
        zis.closeEntry();
        zis.close();
    }
}

Inside the while loop, we’ll iterate through each ZipEntry and first check if it’s a directory. If it is, then we’ll create the directory using the mkdirs() method; otherwise, we’ll continue with creating the file:

while循环中,我们将遍历每个ZipEntry,首先检查它是否是一个目录。如果是,那么我们将使用mkdirs()方法创建目录;否则,我们将继续创建文件。

while (zipEntry != null) {
     File newFile = newFile(destDir, zipEntry);
     if (zipEntry.isDirectory()) {
         if (!newFile.isDirectory() && !newFile.mkdirs()) {
             throw new IOException("Failed to create directory " + newFile);
         }
     } else {
         // fix for Windows-created archives
         File parent = newFile.getParentFile();
         if (!parent.isDirectory() && !parent.mkdirs()) {
             throw new IOException("Failed to create directory " + parent);
         }
         
         // write file content
         FileOutputStream fos = new FileOutputStream(newFile);
         int len;
         while ((len = zis.read(buffer)) > 0) {
             fos.write(buffer, 0, len);
         }
         fos.close();
     }
 zipEntry = zis.getNextEntry();
}

One note here is that on the else branch, we’re also checking if the parent directory of the file exists. This is necessary for archives created on Windows, where the root directories don’t have a corresponding entry in the zip file.

这里要注意的是,在else分支,我们也要检查文件的父目录是否存在。这对于在 Windows 上创建的档案是必要的,因为根目录在 zip 文件中没有相应的条目。

Another key point can be seen in the newFile() method:

另一个关键点可以在newFile()方法中看到。

public static File newFile(File destinationDir, ZipEntry zipEntry) throws IOException {
    File destFile = new File(destinationDir, zipEntry.getName());

    String destDirPath = destinationDir.getCanonicalPath();
    String destFilePath = destFile.getCanonicalPath();

    if (!destFilePath.startsWith(destDirPath + File.separator)) {
        throw new IOException("Entry is outside of the target dir: " + zipEntry.getName());
    }

    return destFile;
}

This method guards against writing files to the file system outside of the target folder. This vulnerability is called Zip Slip, and we can read more about it here.

这种方法可以防止将文件写入目标文件夹之外的文件系统中。这个漏洞被称为Zip Slip,我们可以在这里阅读更多关于它的信息

6. Conclusion

6.结论

In this article, we illustrated how to use Java libraries for zipping and unzipping files.

在这篇文章中,我们说明了如何使用Java库来压缩和解压文件。

The implementation of these examples can be found over on GitHub.

这些例子的实现可以在GitHub上找到over