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 条目,准备写入下一个文件。
相关推荐
Albert Edison32 分钟前
【最新版】IntelliJ IDEA 2025 创建 SpringBoot 项目
java·spring boot·intellij-idea
超级小忍1 小时前
JVM 中的垃圾回收算法及垃圾回收器详解
java·jvm
weixin_446122461 小时前
JAVA内存区域划分
java·开发语言·redis
Piper蛋窝2 小时前
深入 Go 语言垃圾回收:从原理到内建类型 Slice、Map 的陷阱以及为何需要 strings.Builder
后端·go
勤奋的小王同学~2 小时前
(javaEE初阶)计算机是如何组成的:CPU基本工作流程 CPU介绍 CPU执行指令的流程 寄存器 程序 进程 进程控制块 线程 线程的执行
java·java-ee
TT哇2 小时前
JavaEE==网站开发
java·redis·java-ee
2401_826097622 小时前
JavaEE-Linux环境部署
java·linux·java-ee
缘来是庄2 小时前
设计模式之访问者模式
java·设计模式·访问者模式
Bug退退退1233 小时前
RabbitMQ 高级特性之死信队列
java·分布式·spring·rabbitmq
梵高的代码色盘3 小时前
后端树形结构
java