ZIP写入器-ZipWriter
# 📚简介
ZipWriter类是Hutool压缩模块中用于生成Zip文件的工具类,它封装了JDK的ZipOutputStream,提供了更简洁易用的API,用于创建和写入Zip文件。通过ZipWriter,开发者可以轻松地将文件、目录或流添加到Zip文件中,并支持自定义压缩级别、注释和压缩方式。
# 🏗️核心结构
# 1. 类定义
public class ZipWriter implements Closeable {
private File zipFile;
private final ZipOutputStream out;
private int bufferSize = IoUtil.DEFAULT_BUFFER_SIZE;
// ...
}
# 2. 核心属性
| 属性名 | 类型 | 描述 |
|---|---|---|
zipFile | File | 生成的Zip文件,可选 |
out | ZipOutputStream | 底层的Zip输出流 |
bufferSize | int | 自定义缓存大小,默认IoUtil.DEFAULT_BUFFER_SIZE |
# 🔧 API说明
# 1. 创建ZipWriter实例
# of(File zipFile, Charset charset)
从Zip文件创建ZipWriter实例。
参数说明:
zipFile:生成的Zip文件charset:编码方式,用于处理文件名和内容的编码
返回值:
- ZipWriter实例,用于链式调用
# of(OutputStream out, Charset charset)
从输出流创建ZipWriter实例。
参数说明:
out:Zip输出流,一般为文件输出流charset:编码方式,用于处理文件名和内容的编码
返回值:
- ZipWriter实例,用于链式调用
# 2. 配置方法
# setBufferSize(int bufferSize)
自定义压缩缓存大小,特定条件下可调节性能。
参数说明:
bufferSize:缓存大小,单位为字节
返回值:
- ZipWriter实例,用于链式调用
# setLevel(int level)
设置压缩级别,可选1~9,-1表示默认级别。
参数说明:
level:压缩级别- 0:不压缩
- 1:最快压缩
- 9:最高压缩比
- -1:默认级别(5)
返回值:
- ZipWriter实例,用于链式调用
# setComment(String comment)
设置Zip文件的注释。
参数说明:
comment:Zip文件注释
返回值:
- ZipWriter实例,用于链式调用
# setMethod(int method)
设置压缩方式,支持ZipOutputStream.DEFLATED和ZipOutputStream.STORED。
参数说明:
method:压缩方式ZipOutputStream.DEFLATED:默认,使用DEFLATE压缩算法ZipOutputStream.STORED:存储模式,不压缩
返回值:
- ZipWriter实例,用于链式调用
# 3. 获取底层流
# getOut()
获取原始的ZipOutputStream。
返回值:
- 底层的
ZipOutputStream实例
# 4. 添加内容
# add(boolean withSrcDir, FileFilter filter, File... files)
对文件或文件目录进行压缩。
参数说明:
withSrcDir:是否包含被打包目录,只针对压缩目录有效false:只压缩目录下的文件或目录true:将本目录也压缩
filter:文件过滤器,null表示不过滤files:要压缩的源文件或目录
返回值:
- ZipWriter实例,用于链式调用
异常:
IORuntimeException:IO异常
# add(Resource... resources)
添加资源到压缩包,添加后关闭资源流。
参数说明:
resources:需要压缩的资源,资源的路径为Resource.getName()
返回值:
- ZipWriter实例,用于链式调用
异常:
IORuntimeException:IO异常
# add(String path, InputStream in)
添加文件流到压缩包,添加后关闭输入文件流。
参数说明:
path:压缩的路径,null和""表示根目录下in:需要压缩的输入流,使用完后自动关闭,null表示加入空目录
返回值:
- ZipWriter实例,用于链式调用
异常:
IORuntimeException:IO异常
# add(String[] paths, InputStream[] ins)
对流中的数据加入到压缩文件,路径列表和流列表长度必须一致。
参数说明:
paths:流数据在压缩文件中的路径或文件名ins:要压缩的源,添加完成后自动关闭流
返回值:
- ZipWriter实例,用于链式调用
异常:
IllegalArgumentException:路径或流为空,或长度不一致IORuntimeException:IO异常
# 5. 资源关闭
# close()
关闭ZipWriter,完成Zip文件的写入。
异常:
IORuntimeException:IO异常
实现逻辑:
- 调用
out.finish()完成Zip文件的写入 - 静默关闭底层的
ZipOutputStream
# 🚀 使用示例
# 1. 创建ZipWriter并添加文件
// 创建ZipWriter
File zipFile = FileUtil.file("test.zip");
try (ZipWriter writer = ZipWriter.of(zipFile, CharsetUtil.CHARSET_UTF_8)) {
// 添加单个文件
writer.add("file1.txt", FileUtil.getInputStream("file1.txt"));
// 添加目录,包含目录本身
writer.add(true, null, FileUtil.file("sourceDir"));
Console.log("Zip文件创建完成:{}", zipFile.getAbsolutePath());
}
# 2. 自定义压缩配置
// 自定义压缩配置
try (ZipWriter writer = ZipWriter.of(FileUtil.file("custom.zip"), CharsetUtil.CHARSET_UTF_8)) {
// 设置最高压缩级别
writer.setLevel(9);
// 设置Zip文件注释
writer.setComment("这是一个测试Zip文件");
// 设置压缩方式为DEFLATED(默认)
writer.setMethod(ZipOutputStream.DEFLATED);
// 设置缓存大小
writer.setBufferSize(8192);
writer.add("file.txt", FileUtil.getInputStream("file.txt"));
Console.log("自定义配置的Zip文件创建完成!");
}
# 3. 从流创建ZipWriter
// 从输出流创建ZipWriter
try (OutputStream out = FileUtil.getOutputStream("stream.zip");
ZipWriter writer = ZipWriter.of(out, CharsetUtil.CHARSET_UTF_8)) {
writer.add("file.txt", FileUtil.getInputStream("file.txt"));
Console.log("从流创建的Zip文件完成!");
}
# 4. 添加多个文件和目录
// 添加多个文件和目录
try (ZipWriter writer = ZipWriter.of(FileUtil.file("multi.zip"), CharsetUtil.CHARSET_UTF_8)) {
// 添加多个文件
writer.add("file1.txt", FileUtil.getInputStream("file1.txt"))
.add("file2.txt", FileUtil.getInputStream("file2.txt"))
.add("file3.txt", FileUtil.getInputStream("file3.txt"));
// 添加目录,不包含目录本身
writer.add(false, null, FileUtil.file("dir1"), FileUtil.file("dir2"));
Console.log("添加多个文件和目录的Zip文件完成!");
}
# 5. 使用文件过滤器
// 使用文件过滤器,只压缩txt文件
try (ZipWriter writer = ZipWriter.of(FileUtil.file("filtered.zip"), CharsetUtil.CHARSET_UTF_8)) {
writer.add(true, file -> {
// 只压缩文件,不压缩目录
// 或者只压缩特定后缀的文件
return !file.isDirectory() || file.getName().endsWith(".txt");
}, FileUtil.file("sourceDir"));
Console.log("使用过滤器的Zip文件完成!");
}
# 6. 添加空目录
// 添加空目录
try (ZipWriter writer = ZipWriter.of(FileUtil.file("emptyDir.zip"), CharsetUtil.CHARSET_UTF_8)) {
// 添加空目录,path以"/"结尾
writer.add("emptyDir/", null);
// 或者直接传null作为InputStream
writer.add("anotherEmptyDir", null);
Console.log("包含空目录的Zip文件完成!");
}
# 7. 批量添加流
// 批量添加流
try (ZipWriter writer = ZipWriter.of(FileUtil.file("batch.zip"), CharsetUtil.CHARSET_UTF_8)) {
// 准备数据
String[] paths = {"file1.txt", "file2.txt", "file3.txt"};
ByteArrayInputStream[] ins = {
new ByteArrayInputStream("内容1".getBytes()),
new ByteArrayInputStream("内容2".getBytes()),
new ByteArrayInputStream("内容3".getBytes())
};
// 批量添加
writer.add(paths, ins);
Console.log("批量添加流的Zip文件完成!");
}
# 🎯 设计优势
# 1. 简洁易用的API
- 采用链式调用设计,代码更加简洁流畅
- 封装了复杂的
ZipOutputStream操作,降低了使用门槛 - 提供了多种添加内容的方式,适应不同场景
# 2. 灵活的配置选项
- 支持自定义压缩级别(0~9)
- 支持设置Zip文件注释
- 支持选择压缩方式(DEFLATED或STORED)
- 支持自定义缓存大小
# 3. 多种添加方式
- 支持添加单个文件
- 支持添加目录(包含或不包含目录本身)
- 支持添加资源
- 支持添加流数据
- 支持批量添加
# 4. 自动资源管理
- 实现了
Closeable接口,可以配合try-with-resources使用 - 自动关闭底层流资源,避免资源泄漏
- 自动关闭添加的输入流
# 5. 安全的设计
- 防止将Zip文件本身添加到压缩包中,避免死循环
- 自动处理空目录的情况
- 统一的异常处理,避免了繁琐的try-catch代码
# 6. 高效的性能
- 支持自定义缓存大小,可根据实际情况优化性能
- 内部使用高效的流复制机制
- 支持批量操作,减少IO开销
# 📌 注意事项
# 1. 资源关闭
- 务必关闭ZipWriter实例,否则Zip文件可能不完整
- 建议使用try-with-resources语句自动管理资源
- 关闭ZipWriter时会自动调用
finish()方法完成Zip文件的写入
# 2. 编码问题
- 确保使用正确的编码方式,特别是处理非UTF-8编码的文件名
- 不同操作系统和压缩工具对Zip文件名编码的处理可能不同
- 建议显式指定编码,避免默认编码导致的乱码问题
# 3. 压缩级别选择
- 压缩级别越高,压缩率越高,但压缩速度越慢
- 对于需要快速生成Zip文件的场景,建议使用较低的压缩级别(1~3)
- 对于需要高压缩比的场景,建议使用较高的压缩级别(7~9)
- 默认级别(5)适合大多数场景,平衡了压缩速度和压缩比
# 4. 大文件处理
- 对于超大文件,建议使用默认或较大的缓存大小
- 避免一次性将大文件读入内存,使用流式处理
- 可以考虑在后台线程中执行压缩操作,避免阻塞主线程
# 5. 路径问题
- 确保添加的文件路径在Zip文件中是合理的
- 空目录的路径应该以"/"结尾
- 避免添加绝对路径,Zip文件中的路径应该是相对路径
# 6. 过滤条件
- 使用文件过滤器时,确保过滤条件正确,避免意外排除需要压缩的文件
- 过滤器会应用到所有递归处理的文件和目录
# 🔄 与传统JDK方式的对比
# 1. 传统JDK方式(复杂)
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
// 传统方式创建Zip文件
String zipFilePath = "test.zip";
String[] filesToZip = {"file1.txt", "file2.txt"};
try (ZipOutputStream zos = new ZipOutputStream(
new FileOutputStream(zipFilePath), StandardCharsets.UTF_8)) {
for (String filePath : filesToZip) {
File file = new File(filePath);
ZipEntry zipEntry = new ZipEntry(file.getName());
zos.putNextEntry(zipEntry);
try (FileInputStream fis = new FileInputStream(file)) {
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) > 0) {
zos.write(buffer, 0, len);
}
}
zos.closeEntry();
}
zos.finish();
Console.log("Zip文件创建完成!");
} catch (IOException e) {
e.printStackTrace();
}
# 2. Hutool方式(简洁)
import cn.hutool.v7.core.compress.ZipWriter;
import cn.hutool.v7.core.io.FileUtil;
import cn.hutool.v7.core.util.CharsetUtil;
import cn.hutool.v7.core.util.Console;
// Hutool方式创建Zip文件
String zipFilePath = "test.zip";
String[] filesToZip = {"file1.txt", "file2.txt"};
try (ZipWriter writer = ZipWriter.of(FileUtil.file(zipFilePath), CharsetUtil.CHARSET_UTF_8)) {
for (String filePath : filesToZip) {
writer.add(new File(filePath).getName(), FileUtil.getInputStream(filePath));
}
Console.log("Zip文件创建完成!");
}