ZIP资源-ZipResource
# 📚简介
ZipResource是Hutool压缩模块中的一个接口,用于表示Zip资源,如Zip流资源或Zip文件资源。它定义了统一的API,用于读取Zip文件中的条目和获取指定条目的流,是ZipReader等类的底层支撑。
# 🏗️核心结构
# 1. 接口定义
public interface ZipResource extends Closeable {
void read(final Consumer<ZipEntry> consumer, final int maxSizeDiff);
InputStream get(String path);
InputStream get(ZipEntry entry);
}
# 2. 实现类
# 2.1 ZipFileResource
ZipFileResource是基于ZipFile实现的文件型Zip资源,用于处理文件形式的Zip资源。
核心功能:
- 基于
ZipFile实现,适合处理本地Zip文件 - 提供高效的Zip条目遍历和文件流获取
- 内置ZipBomb防护机制
主要方法:
read(Consumer<ZipEntry> consumer, int maxSizeDiff):遍历Zip条目,支持ZipBomb检查get(String path):根据路径获取文件流get(ZipEntry entry):根据ZipEntry获取文件流close():关闭资源
使用示例:
// 创建ZipFileResource
File zipFile = FileUtil.file("test.zip");
try (ZipFile jdkZipFile = new ZipFile(zipFile, CharsetUtil.CHARSET_UTF_8);
ZipResource resource = new ZipFileResource(jdkZipFile)) {
// 遍历Zip条目
resource.read(entry -> {
Console.log("条目:" + entry.getName());
}, 100);
// 获取文件流
try (InputStream in = resource.get("file.txt")) {
if (in != null) {
// 处理文件流
}
}
}
# 2.2 ZipStreamResource
ZipStreamResource是基于ZipInputStream实现的流型Zip资源,用于处理流形式的Zip资源。
核心功能:
- 基于
ZipInputStream实现,适合处理网络流或其他非文件形式的Zip资源 - 提供流式的Zip条目遍历和文件流获取
- 内置ZipBomb防护机制
主要方法:
read(Consumer<ZipEntry> consumer, int maxSizeDiff):遍历Zip条目,支持ZipBomb检查get(String path):根据路径获取文件流get(ZipEntry entry):根据ZipEntry获取文件流close():关闭资源
使用示例:
// 创建ZipStreamResource
try (InputStream fileIn = FileUtil.getInputStream("test.zip");
ZipInputStream zipIn = new ZipInputStream(fileIn, CharsetUtil.CHARSET_UTF_8);
ZipResource resource = new ZipStreamResource(zipIn)) {
// 遍历Zip条目
resource.read(entry -> {
Console.log("条目:" + entry.getName());
}, 100);
// 获取文件流
try (InputStream in = resource.get("file.txt")) {
if (in != null) {
// 处理文件流
}
}
}
# 3. 其他辅助类
压缩模块还包含一些辅助类,用于支持核心功能:
| 类名 | 描述 |
|---|---|
ZipSecurityUtil | Zip安全工具,提供ZipBomb防护机制 |
InflaterInputStream | 增强的Inflater输入流 |
OpcZipOutputStream | OPC格式Zip输出流,用于Excel等Office文档 |
ZipCopyVisitor | Zip复制访问器,用于复制Zip条目 |
ZipFileResource | 文件型Zip资源,基于ZipFile实现 |
ZipStreamResource | 流型Zip资源,基于ZipInputStream实现 |
# 🔧 API说明
# 1. 遍历Zip条目
# read(Consumer<ZipEntry> consumer, int maxSizeDiff)
读取并处理Zip文件中的每一个ZipEntry。
参数说明:
consumer:ZipEntry处理器,用于处理每个Zip条目maxSizeDiff:检查ZipBomb文件差异倍数,-1表示不检查ZipBomb
说明:
- ZipBomb是一种恶意压缩文件,解压后体积会急剧膨胀,可能导致系统资源耗尽
- 差异倍数 = 解压后大小 / 压缩前大小
- 设置合理的
maxSizeDiff可以有效防止ZipBomb攻击
# 2. 获取文件流
# get(String path)
获取指定路径的文件流。
参数说明:
path:文件在Zip中的路径,相对于Zip根目录
返回值:
- 文件输入流,可以读取文件内容
实现逻辑:
- 如果是文件模式,直接获取Entry对应的流
- 如果是流模式,遍历entry后,找到对应流返回
# get(ZipEntry entry)
获取指定ZipEntry对应的文件流。
参数说明:
entry:ZipEntry对象,表示Zip文件中的一个条目
返回值:
- 文件输入流,可以读取文件内容
# 3. 资源关闭
# close()
关闭ZipResource,释放资源。
说明:
- 继承自
Closeable接口 - 具体实现由子类提供
# 🎯 设计优势
# 1. 统一的资源访问接口
- 为不同类型的Zip资源提供统一的访问接口
- 隐藏了底层实现细节,如基于文件还是基于流
- 便于上层代码使用,无需关心具体实现
# 2. 灵活的遍历方式
- 支持通过
Consumer函数式接口处理Zip条目 - 提供了ZipBomb防护机制
- 遍历逻辑由具体实现类优化
# 3. 多种获取文件流的方式
- 支持通过路径获取文件流
- 支持通过
ZipEntry对象获取文件流 - 适应不同的使用场景
# 4. 自动资源管理
- 继承自
Closeable接口,可以配合try-with-resources使用 - 确保资源正确关闭,避免资源泄漏
# 📌 注意事项
# 1. 实现类的选择
ZipFileResource适合处理文件型Zip资源,性能较好ZipStreamResource适合处理流型Zip资源,如网络流- 根据实际场景选择合适的实现类
# 2. ZipBomb防护
- 建议开启ZipBomb检查,特别是处理不可信来源的Zip文件
- 根据实际情况调整差异倍数,默认100倍
- 对于合法的高压缩比文件,可以适当增大差异倍数或关闭检查
# 3. 资源关闭
- 务必关闭ZipResource实例,释放系统资源
- 建议使用try-with-resources语句自动管理资源
- 不关闭资源可能导致文件句柄泄漏
# 4. 流的使用
- 获取的文件流需要手动关闭,或者使用try-with-resources管理
- 同一ZipResource实例可以获取多个文件流,但需要注意流的关闭顺序
- 某些实现可能不支持并发获取多个文件流
# 🔄 与传统JDK方式的对比
# 1. 传统JDK方式(复杂)
// 传统方式处理Zip资源
String zipFilePath = "test.zip";
// 方式1:使用ZipFile
Console.log("=== 使用ZipFile ===");
try (ZipFile zipFile = new ZipFile(zipFilePath, StandardCharsets.UTF_8)) {
zipFile.stream().forEach(entry -> {
Console.log("条目:" + entry.getName());
if (!entry.isDirectory()) {
try (InputStream in = zipFile.getInputStream(entry)) {
// 处理文件流
Console.log("文件大小:" + entry.getSize());
} catch (IOException e) {
e.printStackTrace();
}
}
});
} catch (IOException e) {
e.printStackTrace();
}
// 方式2:使用ZipInputStream
Console.log("=== 使用ZipInputStream ===");
try (ZipInputStream zis = new ZipInputStream(
new FileInputStream(zipFilePath), StandardCharsets.UTF_8)) {
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
Console.log("条目:" + entry.getName());
if (!entry.isDirectory()) {
// 处理文件流
Console.log("文件大小:" + entry.getSize());
}
zis.closeEntry();
}
} catch (IOException e) {
e.printStackTrace();
}
# 2. Hutool方式(统一接口)
// Hutool方式处理Zip资源
String zipFilePath = "test.zip";
// 方式1:使用ZipFileResource
Console.log("=== 使用ZipFileResource ===");
try (ZipResource resource = new ZipFileResource(
FileUtil.getZipFile(zipFilePath, CharsetUtil.CHARSET_UTF_8))) {
resource.read(entry -> {
Console.log("条目:{}", entry.getName());
if (!entry.isDirectory()) {
try (InputStream in = resource.get(entry)) {
// 处理文件流
Console.log("文件大小:{}", entry.getSize());
} catch (IOException e) {
e.printStackTrace();
}
}
}, 100);
}
// 方式2:使用ZipStreamResource
Console.log("=== 使用ZipStreamResource ===");
try (ZipResource resource = new ZipStreamResource(
FileUtil.getZipInputStream(zipFilePath, CharsetUtil.CHARSET_UTF_8))) {
resource.read(entry -> {
Console.log("条目:{}", entry.getName());
if (!entry.isDirectory()) {
try (InputStream in = resource.get(entry.getName())) {
// 处理文件流
Console.log("文件路径:{}", entry.getName());
} catch (IOException e) {
e.printStackTrace();
}
}
}, -1); // 不检查ZipBomb
}