简绍
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();