锋盈数科-知识库 Logo
首页
软件开发
计算机基础
Hello Halo
新手必读
关于本知识库
登录 →
锋盈数科-知识库 Logo
首页 软件开发 计算机基础 Hello Halo 新手必读 关于本知识库
登录
  1. 首页
  2. 软件开发
  3. JAVA
  4. Java 打包 ZIP 文件详解

Java 打包 ZIP 文件详解

0
  • JAVA
  • 发布于 2024-08-02
  • 10 次阅读
黄健
黄健

原文链接:https://blog.csdn.net/weixin_41883161/article/details/139591781

在软件开发中,经常需要对文件或文件夹进行压缩打包,以便于存储、传输或备份。ZIP 是一种常见的压缩格式,它具有高效的压缩比和广泛的兼容性。本文将详细介绍如何使用 Java 语言进行 ZIP 文件的创建、读取和操作,并涵盖一些高级技巧和最佳实践。

一、ZIP 格式简介

ZIP 文件格式是一种用于数据压缩和存档的文件格式,由 PKWARE 公司在 1989 年首次推出。它通过压缩算法减少文件体积,同时支持多个文件和文件夹的打包。ZIP 文件具有以下特点:

多文件打包:支持将多个文件和文件夹打包成一个 ZIP 文件。

压缩算法:常用的压缩算法有 Deflate、Bzip2 和 LZMA 等。

平台无关性:ZIP 文件可以在不同操作系统之间传输而不影响数据的完整性。

高效解压缩:解压缩速度较快,支持随机访问文件内容。

二、Java 中处理 ZIP 文件的基本库

Java 提供了丰富的标准库用于处理 ZIP 文件,其中最主要的是 java.util.zip 包。该包包含了一系列类和接口,用于创建、读取和操作 ZIP 文件。常用的类包括:

ZipInputStream 和 ZipOutputStream:用于顺序读取和写入 ZIP 文件。

ZipFile 和 ZipEntry:用于随机访问 ZIP 文件中的条目。

下面我们将通过具体实例,详细讲解如何使用这些类进行 ZIP 文件的操作。

三、创建 ZIP 文件

3.1 使用 ZipOutputStream 创建 ZIP 文件

ZipOutputStream 是一个用于将文件压缩到 ZIP 格式的输出流。我们可以通过它将多个文件和文件夹压缩到一个 ZIP 文件中。以下是一个简单的示例,展示如何使用 ZipOutputStream 创建一个 ZIP 文件:

import java.io.*;

import java.util.zip.*;

public class ZipExample {

    public static void main(String[] args) {

        String sourceFile = "src";

        String zipFile = "archive.zip";

        try (FileOutputStream fos = new FileOutputStream(zipFile);

             ZipOutputStream zos = new ZipOutputStream(fos)) {

            File fileToZip = new File(sourceFile);

            zipFile(fileToZip, fileToZip.getName(), zos);

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

    private static void zipFile(File fileToZip, String fileName, ZipOutputStream zos) throws IOException {

        if (fileToZip.isHidden()) {

            return;

        }

        if (fileToZip.isDirectory()) {

            if (fileName.endsWith("/")) {

                zos.putNextEntry(new ZipEntry(fileName));

                zos.closeEntry();

            } else {

                zos.putNextEntry(new ZipEntry(fileName + "/"));

                zos.closeEntry();

            }

            File[] children = fileToZip.listFiles();

            for (File childFile : children) {

                zipFile(childFile, fileName + "/" + childFile.getName(), zos);

            }

            return;

        }

        try (FileInputStream fis = new FileInputStream(fileToZip)) {

            ZipEntry zipEntry = new ZipEntry(fileName);

            zos.putNextEntry(zipEntry);

            byte[] bytes = new byte[1024];

            int length;

            while ((length = fis.read(bytes)) >= 0) {

                zos.write(bytes, 0, length);

            }

        }

    }

}

上述代码实现了一个递归压缩目录及其内容的功能。zipFile 方法会遍历目录中的所有文件和子目录,并将它们逐一压缩到 ZIP 文件中。

3.2 压缩单个文件

有时候我们只需要压缩单个文件,而不是整个目录。这种情况下,可以简化上述代码,只需要处理文件本身:

import java.io.*;

import java.util.zip.*;

public class ZipSingleFileExample {

    public static void main(String[] args) {

        String sourceFile = "document.txt";

        String zipFile = "document.zip";

        try (FileOutputStream fos = new FileOutputStream(zipFile);

             ZipOutputStream zos = new ZipOutputStream(fos);

             FileInputStream fis = new FileInputStream(sourceFile)) {

            ZipEntry zipEntry = new ZipEntry(sourceFile);

            zos.putNextEntry(zipEntry);

            byte[] buffer = new byte[1024];

            int length;

            while ((length = fis.read(buffer)) >= 0) {

                zos.write(buffer, 0, length);

            }

            zos.closeEntry();

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

}

该示例展示了如何将一个文件 document.txt 压缩成 document.zip。通过 FileInputStream 读取源文件的数据,并将其写入 ZipOutputStream 中。

四、读取 ZIP 文件

4.1 使用 ZipInputStream 读取 ZIP 文件

ZipInputStream 是一个用于解压缩 ZIP 文件的输入流。它提供了逐个读取 ZIP 条目的功能,可以遍历并读取 ZIP 文件中的每一个条目。以下是一个示例,展示如何使用 ZipInputStream 读取 ZIP 文件中的内容:

import java.io.*;

import java.util.zip.*;

public class UnzipExample {

    public static void main(String[] args) {

        String zipFile = "archive.zip";

        String destDir = "output";

        File dir = new File(destDir);

        if (!dir.exists()) dir.mkdirs();

        try (FileInputStream fis = new FileInputStream(zipFile);

             ZipInputStream zis = new ZipInputStream(fis)) {

            ZipEntry zipEntry = zis.getNextEntry();

            while (zipEntry != null) {

                File newFile = newFile(dir, 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);

                    }

                    try (FileOutputStream fos = new FileOutputStream(newFile)) {

                        int len;

                        byte[] buffer = new byte[1024];

                        while ((len = zis.read(buffer)) > 0) {

                            fos.write(buffer, 0, len);

                        }

                    }

                }

                zipEntry = zis.getNextEntry();

            }

            zis.closeEntry();

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

    private 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;

    }

}

该示例展示了如何使用 ZipInputStream 解压缩 archive.zip 文件,并将其内容提取到指定的目录 output 中。newFile 方法用于防止路径遍历漏洞,确保解压后的文件位于目标目录中。

4.2 使用 ZipFile 读取 ZIP 文件

ZipFile 类提供了更为高效的随机访问功能,可以直接访问 ZIP 文件中的任意条目,而不需要遍历整个 ZIP 文件。以下是一个示例,展示如何使用 ZipFile 读取 ZIP 文件中的内容:

import java.io.*;

import java.util.Enumeration;

import java.util.zip.*;

public class ReadZipFileExample {

    public static void main(String[] args) {

        String zipFile = "archive.zip";

        try (ZipFile zip = new ZipFile(zipFile)) {

            Enumeration<? extends ZipEntry> entries = zip.entries();

            while (entries.hasMoreElements()) {

                ZipEntry entry = entries.nextElement();

                System.out.println("Extracting: " + entry.getName());

                File file = new File("output/" + entry.getName());

                if (entry.isDirectory()) {

                    file.mkdirs();

                    continue;

                }

                try (InputStream is = zip.getInputStream(entry);

                     FileOutputStream fos = new FileOutputStream(file)) {

                    byte[] buffer = new byte[1024];

                    int length;

                    while ((length = is.read(buffer)) > 0) {

                        fos.write(buffer, 0, length);

                    }

                }

            }

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

}

该示例展示了如何使用 ZipFile 类读取 archive.zip 文件中的内容,并将其解压到 output 目录中。

五、ZIP 文件操作的高级技巧

5.1 设置压缩级别

在创建 ZIP 文件时,我们可以通过 ZipOutputStream 设置压缩级别,从而控制压缩率和压缩速度。Java 提供了三种压缩级别:

1

. ZipOutputStream.STORED:不进行压缩,仅存储文件。

2. ZipOutputStream.DEFLATED:使用 Deflate 算法进行压缩(默认)。

3. ZipOutputStream.NO_COMPRESSION:无压缩。

以下示例展示了如何设置压缩级别:

import java.io.*;

import java.util.zip.*;

public class ZipWithCompressionLevelExample {

    public static void main(String[] args) {

        String sourceFile = "document.txt";

        String zipFile = "document.zip";

        try (FileOutputStream fos = new FileOutputStream(zipFile);

             ZipOutputStream zos = new ZipOutputStream(fos);

             FileInputStream fis = new FileInputStream(sourceFile)) {

            zos.setLevel(ZipOutputStream.DEFLATED);  // 设置压缩级别

            ZipEntry zipEntry = new ZipEntry(sourceFile);

            zos.putNextEntry(zipEntry);

            byte[] buffer = new byte[1024];

            int length;

            while ((length = fis.read(buffer)) >= 0) {

                zos.write(buffer, 0, length);

            }

            zos.closeEntry();

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

}

5.2 添加注释

ZIP 文件支持在文件级和条目级添加注释。以下是一个示例,展示如何为 ZIP 文件和 ZIP 条目添加注释:

import java.io.*;

import java.util.zip.*;

public class ZipWithCommentsExample {

    public static void main(String[] args) {

        String sourceFile = "document.txt";

        String zipFile = "document_with_comments.zip";

        try (FileOutputStream fos = new FileOutputStream(zipFile);

             ZipOutputStream zos = new ZipOutputStream(fos);

             FileInputStream fis = new FileInputStream(sourceFile)) {

            ZipEntry zipEntry = new ZipEntry(sourceFile);

            zipEntry.setComment("This is a comment for the entry");

            zos.putNextEntry(zipEntry);

            byte[] buffer = new byte[1024];

            int length;

            while ((length = fis.read(buffer)) >= 0) {

                zos.write(buffer, 0, length);

            }

            zos.closeEntry();

            zos.setComment("This is a comment for the ZIP file");

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

}

5.3 使用密码保护 ZIP 文件

标准的 java.util.zip 包并不支持加密,但可以使用第三方库(如 Apache Commons Compress 或 Zip4j)来实现密码保护功能。以下是使用 Zip4j 库实现加密压缩的示例:

import net.lingala.zip4j.core.ZipFile;

import net.lingala.zip4j.exception.ZipException;

import net.lingala.zip4j.model.ZipParameters;

import net.lingala.zip4j.util.Zip4jConstants;

public class ZipWithPasswordExample {

    public static void main(String[] args) {

        String sourceFile = "document.txt";

        String zipFile = "document_encrypted.zip";

        String password = "securepassword";

        try {

            ZipFile zip = new ZipFile(zipFile);

            ZipParameters parameters = new ZipParameters();

            parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);

            parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);

            parameters.setEncryptFiles(true);

            parameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_AES);

            parameters.setAesKeyStrength(Zip4jConstants.AES_STRENGTH_256);

            parameters.setPassword(password);

            zip.addFile(new File(sourceFile), parameters);

        } catch (ZipException e) {

            e.printStackTrace();

        }

    }

}

以上示例展示了如何使用 Zip4j 库对 ZIP 文件进行 AES 加密,并设置密码保护。

六、最佳实践

6.1 处理大文件

在处理大文件时,应避免将整个文件读入内存。可以采用缓冲区的方式逐步读取和写入数据,以减少内存占用。例如,在读取和写入过程中使用较大的缓冲区(如 4KB 或 8KB)以提高效率。

6.2 错误处理

应始终处理可能出现的 IOException 异常,并在必要时提供有用的错误信息。可以使用日志记录框架(如 Log4j 或 SLF4J)来记录错误信息,便于调试和维护。

6.3 资源管理

应确保在完成 ZIP 文件操作后,正确关闭所有打开的流和资源。可以使用 Java 7 引入的 try-with-resources 语法来简化资源管理,确保在块结束时自动关闭资源。

6.4 防止路径遍历漏洞

在解压缩 ZIP 文件时,应注意防止路径遍历攻击,确保解压后的文件位于预期的目标目录中。可以通过检查文件的规范路径来实现这一点,防止恶意 ZIP 文件利用相对路径将文件解压到不安全的位置。

七、总结

本文详细介绍了如何使用 Java 进行 ZIP 文件的创建、读取和操作,涵盖了基本用法和高级技巧。通过合理使用 java.util.zip 包和第三方库,我们可以高效地处理 ZIP 文件,并应用密码保护和注释等高级功能。在实际应用中,遵循最佳实践可以提高程序的健壮性和安全性。

标签: #JAVA 991
相关文章

Spring 实现 3 种异步接口 2024-10-18 09:07

大家好,我是苏三~ 如何处理比较耗时的接口? 这题我熟,直接上异步接口,使用 Callable、WebAsyncTask 和 DeferredResult、CompletableFuture等均可实现。 但这些方法有局限性,处理结果仅返回单个值。在某些场景下,如果需要接口异步处理的同时,还持续不断地

重学SpringBoot3-集成Redis(五)之布隆过滤器 2024-10-08 11:24

更多SpringBoot3内容请关注我的专栏:《SpringBoot3》 期待您的点赞👍收藏⭐评论✍ 重学SpringBoot3-集成Redis(五)之布隆过滤器 1. 什么是布隆过滤器? * 基本概念 适用场景 2. 使用 Redis 实现布隆过滤器 * 项目依赖 Redis 配置

SpringBoot整合异步任务执行 2024-10-08 11:24

同步任务: 同步任务是在单线程中按顺序执行,每次只有一个任务在执行,不会引发线程安全和数据一致性等 并发问题 同步任务需要等待任务执行完成后才能执行下一个任务,无法同时处理多个任务,响应慢,影响用 户体验 异步任务: 异步任务是在多线程中同时执行,多个任务可以并发执行,同时处理多个请求,响应快,资源

springboot kafka多数据源,通过配置动态加载发送者和消费者 2024-10-08 11:24

前言 最近做项目,需要支持kafka多数据源,实际上我们也可以通过代码固定写死多套kafka集群逻辑,但是如果需要不修改代码扩展呢,因为kafka本身不处理额外逻辑,只是起到削峰,和数据的传递,那么就需要对架构做一定的设计了。 准备test kafka本身非常容易上手,如果我们需要单元测试,引入ja

SpringBoot 集成 Redis 2024-10-08 11:24

一:SpringBoot 集成 Redis ①Redis是一个 NoSQL(not only)数据库, 常作用缓存 Cache 使用。 ②Redis是一个中间件、是一个独立的服务器;常用的数据类型: string , hash ,set ,zset , list ③通过Redis客户端可以使用多种语

SpringBoot整合QQ邮箱 2024-10-08 11:24

SpringBoot可以通过导入依赖的方式集成多种技术,这当然少不了我们常用的邮箱,现在本章演示SpringBoot整合QQ邮箱发送邮件…. 下面按步骤进行: 1.获取QQ邮箱授权码 1.1 登录QQ邮箱 1.2 开启SMTP服务 找到下图中的SMTP服务区域,如果当前账号未开启的话自己手动开启。

目录

IT 外包服务商

  • 意见投递
  • zyf6619

软件开发应用

主菜单

  • 首页
  • 软件开发
  • 计算机基础
  • Hello Halo
  • 新手必读
  • 关于本知识库
Copyright © 2024 your company All Rights Reserved. Powered by Halo.