Java高效压缩技巧:ZipOutputStream详解

简绍

ZipOutputStream 是 Java 标准库中用于 创建 ZIP 文件 的核心类,位于 java.util.zip 包中。它允许你将多个文件或字节流写入一个 ZIP 压缩包,并支持设置压缩级别、编码方式等参数。

基本概念

ZipOutputStream 的作用:

  • 将多个文件(或字节数组)打包成一个 ZIP 文件。
  • 支持添加多个条目(ZipEntry),每个条目代表 ZIP 中的一个文件。
  • 可控制压缩级别、编码格式、注释等。

常用方法说明

方法 描述
putNextEntry(ZipEntry entry) 开始写入一个新的 ZIP 条目(文件)
closeEntry() 关闭当前 ZIP 条目,准备写入下一个
write(byte[] b, int off, int len) 写入当前条目的内容(字节数据)
setLevel(int level) 设置压缩级别(0~9)
setComment(String comment) 设置整个 ZIP 文件的注释
finish() 完成写入,但不关闭底层输出流(可选)

可选的压缩级别(取值范围:0 ~ 9)

级别 含义 特点
Deflater.NO_COMPRESSION (0) 不压缩 速度最快,ZIP 文件最大
Deflater.BEST_SPEED (1) 最快压缩 压缩率低,速度快
Deflater.DEFAULT_COMPRESSION (6) 默认压缩 平衡压缩率与速度
Deflater.BEST_COMPRESSION (9) 最佳压缩 压缩率最高,速度最慢

本地保存

java 复制代码
import java.io.*;
import java.nio.file.*;
import java.util.zip.*;

public class ZipDemo {
    public static void main(String[] args) throws IOException {
        // 源文件夹路径:要压缩的文件所在的目录
        String sourceDir = "path/to/folder";

        // 输出 ZIP 文件路径:生成的压缩包保存的位置和名称
        String zipFilePath = "output.zip";

        // 使用 try-with-resources 自动关闭资源(FileOutputStream 和 ZipOutputStream)
        try (FileOutputStream fos = new FileOutputStream(zipFilePath);
             ZipOutputStream zos = new ZipOutputStream(fos)) {

            // 设置 ZIP 压缩级别为最佳压缩(压缩率最高,但速度较慢)
            zos.setLevel(Deflater.BEST_COMPRESSION);

            // 遍历源目录下的所有文件(包括子目录中的文件)
            Files.walk(Paths.get(sourceDir))
                 // 过滤掉目录,只保留文件
                 .filter(path -> !Files.isDirectory(path))
                 // 对每个文件执行以下操作
                 .forEach(path -> {
                     try {
                         // 构建相对路径字符串:sourceDir + 文件分隔符 + 文件名
                         // 注意:这会导致 ZIP 中的路径包含 sourceDir 的前缀
                         String relativePath = sourceDir + File.separator + path.getFileName();

                         // 创建一个 ZIP 条目(即 ZIP 包中的一个文件)
                         ZipEntry zipEntry = new ZipEntry(relativePath);

                         // 开始写入该条目到 ZIP 流中
                         zos.putNextEntry(zipEntry);

                         // 读取文件内容为字节数组
                         byte[] bytes = Files.readAllBytes(path);

                         // 将文件内容写入 ZIP 输出流
                         zos.write(bytes, 0, bytes.length);

                         // 关闭当前 ZIP 条目,准备写入下一个文件
                         zos.closeEntry();

                     } catch (IOException e) {
                         // 如果写入失败,打印错误信息并输出异常堆栈
                         System.err.println("写入 ZIP 失败: " + path);
                         e.printStackTrace();
                     }
                 });
        }
        // try-with-resources 会自动关闭 fos 和 zos,无需手动 close()
    }
}

ZIP 文件网络流下载

java 复制代码
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment; filename=\"streamed_files.zip\"");

try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream(), StandardCharsets.UTF_8)) {
    zipOut.setLevel(Deflater.BEST_SPEED);

    for (User u : userList) {
        String fileName = String.format("%sxxxx.docx", u.getNickName());

        try {
            zipOut.putNextEntry(new ZipEntry(fileName));
            byte[] docBytes = genDocx(u).toByteArray();
            zipOut.write(docBytes, 0, docBytes.length);
            zipOut.closeEntry();
        } catch (IOException e) {
            log.error("写入 ZIP 条目失败: {}", fileName, e);
            throw new IOException("生成 ZIP 条目时出错: " + fileName, e);
        }
    }

} catch (IOException ex) {
    log.error("导出 ZIP 异常", ex);
    throw new RuntimeException("导出异常");
}

1. 设置响应头

java 复制代码
response.setContentType("application/zip");
  • 设置 HTTP 响应内容类型为 application/zip,告诉浏览器这是一个 ZIP 文件。
  • 浏览器收到后会识别为压缩文件,通常会触发下载行为。
java 复制代码
response.setHeader("Content-Disposition", "attachment; filename=\"streamed_files.zip\"");
  • 设置响应头 Content-Disposition,值为 attachment 表示让浏览器不要在页面中显示该内容,而是提示用户下载。
  • filename="streamed_files.zip" 指定下载时的默认文件名。

2. 创建 ZIP 输出流(使用 try-with-resources)

java 复制代码
try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream(), StandardCharsets.UTF_8)) {
  • 使用 try-with-resources 自动关闭资源,确保最后流会被正确关闭。
  • 创建了一个 ZipOutputStream,它是一个用于写入 ZIP 文件格式的输出流。
  • 第二个参数 StandardCharsets.UTF_8 是为了支持中文文件名编码(避免解压时出现乱码)。
    • ⚠️ 注意:部分旧系统或工具(如 Windows 自带解压器)可能不识别 UTF-8 编码的 ZIP 文件名,建议使用 7-Zip、WinRAR 或 Mac 解压工具。

3. 设置压缩级别(可选)

java 复制代码
zipOut.setLevel(Deflater.BEST_SPEED);
  • 设置压缩级别为最快压缩模式。
  • 这样可以减少服务器 CPU 占用,适合对压缩率要求不高但希望快速响应用户的场景。
  • 可替换为:
    • Deflater.DEFAULT_COMPRESSION(默认)
    • Deflater.BEST_COMPRESSION(最佳压缩)

4. 生成文件加入到 zip 流

java 复制代码
zipOut.putNextEntry(new ZipEntry(fileName));
  • 在 ZIP 包中创建一个新的条目(即文件),名字是上面生成的文件名。
  • 每次调用 putNextEntry() 后必须调用 closeEntry() 才能继续添加下一个文件。
java 复制代码
byte[] docBytes = genDocx(u).toByteArray();
zipOut.write(docBytes, 0, docBytes.length);
  • 调用 genDocx(u) 方法生成当前用户的 .docx 文件内容(返回的是 ByteArrayOutputStream)。
  • 将其转为字节数组后写入 ZIP 流中。
java 复制代码
zipOut.closeEntry();
  • 关闭当前 ZIP 条目,准备写入下一个文件。
相关推荐
yaoxin5211239 分钟前
212. Java 函数式编程风格 - Java 编程风格转换:命令式 vs 函数式(以循环为例)
java·开发语言
摇滚侠20 分钟前
Spring Boot 3零基础教程,WEB 开发 Thymeleaf 属性优先级 行内写法 变量选择 笔记42
java·spring boot·笔记
滑水滑成滑头21 分钟前
**发散创新:多智能体系统的探索与实践**随着人工智能技术的飞速发展,多智能体系统作为当今研究的热点领域,正受到越来越多关注
java·网络·人工智能·python
摇滚侠24 分钟前
Spring Boot 3零基础教程,WEB 开发 Thymeleaf 总结 热部署 常用配置 笔记44
java·spring boot·笔记
十年小站24 分钟前
一、新建一个SpringBoot3项目
java·spring boot
2401_8414956427 分钟前
【数据结构】最长的最短路径的求解
java·数据结构·c++·python·算法·最短路径·图搜索
麦麦鸡腿堡28 分钟前
Java的代码块介绍与快速入门
java·开发语言
PFinal社区_南丞31 分钟前
构建可维护的正则表达式系统-pfinal-regex-center设计与实现
后端·php
Imnobody31 分钟前
吴恩达 Prompt 工程课精讲②:写出高可靠 Prompt 的2大黄金法则
后端
yuuki23323336 分钟前
【C语言】程序的编译和链接(基础向)
c语言·后端