使用ZipInputStream解压多级目录ZIP文件需先解析ZipEntry路径并递归创建父目录;通过getName().endsWith("/")区分目录与文件;用normalize()校验防路径穿越;目录调用createDirectories,文件先建父目录再copy。

使用 ZipInputStream 解压含多级目录的 ZIP 文件,关键在于**正确解析每个 ZipEntry 的路径,并递归创建缺失的父目录**。Java 原生不自动建目录,必须手动处理路径和文件/目录类型。
识别并区分文件与目录
ZipEntry 的名称(getName())末尾带 / 表示这是一个目录条目;否则是普通文件。注意:ZIP 规范中目录本身也是独立条目,不能忽略。
- 调用
zipIn.getNextEntry()获取每个条目 - 用
entry.getName().endsWith("/")判断是否为目录 - 目录条目无需解压内容,只需创建对应文件夹
安全构建目标路径,防止路径穿越
直接拼接 entry.getName() 可能导致路径穿越(如 ../etc/passwd),必须做校验:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 用
Paths.get(entry.getName()).normalize()标准化路径 - 检查标准化后路径是否仍以
..开头或包含非法上溯 - 推荐用
Files.isSameFile(Paths.get("."), targetPath.getParent())或逐段验证 - 更稳妥做法:提取所有路径段,过滤掉
".."和".",再拼接
递归创建目录,再写入文件
对每个条目:
立即学习“Java免费学习笔记(深入)”;
- 若为目录:用
Files.createDirectories(targetDir)创建完整路径(自动递归) - 若为文件:先确保其父目录存在(
Files.createDirectories(file.getParent())),再用Files.write()或FileOutputStream写入内容 - 注意保留原始权限(Java 7+ 可通过
PosixFilePermissions设置,但 Windows 不生效)
完整操作示例(核心逻辑)
以下为关键片段(省略异常处理和资源关闭,实际需用 try-with-resources):
try (ZipInputStream zipIn = new ZipInputStream(new FileInputStream(zipFile))) {
ZipEntry entry;
while ((entry = zipIn.getNextEntry()) != null) {
Path targetPath = outputDir.resolve(entry.getName()).normalize();
// 防穿越:确保路径在 outputDir 下
if (!targetPath.startsWith(outputDir.toAbsolutePath().normalize())) {
throw new IOException("Bad path: " + entry.getName());
}
if (entry.isDirectory()) {
Files.createDirectories(targetPath);
} else {
Files.createDirectories(targetPath.getParent());
Files.copy(zipIn, targetPath, StandardCopyOption.REPLACE_EXISTING);
}
zipIn.closeEntry();
}
}

















