0

0

如何使用Java实现文件压缩与解压_JavaIO综合项目讲解

P粉602998670

P粉602998670

发布时间:2026-01-17 10:04:39

|

304人浏览过

|

来源于php中文网

原创

java.util.zip可完成ZIP压缩解压但需手动处理流、编码、目录结构和中文路径;压缩时路径须标准化、递归处理目录、正确调用putNextEntry/closeEntry;解压需防路径穿越、校验合法性;中文名需统一UTF-8或按需fallback GBK;Jar类仅适用于Java应用打包。

如何使用java实现文件压缩与解压_javaio综合项目讲解

Java 标准库的 java.util.zip 包能完成基础 ZIP 压缩与解压,无需第三方依赖,但必须手动处理流、编码、目录结构和中文路径问题——多数失败都卡在这四点上。

ZipOutputStream 压缩多个文件或目录

核心是把每个条目(ZipEntry)写入输出流,注意路径标准化和文件内容读取顺序:

  • ZipEntryname 必须用正斜杠 / 分隔,不能含盘符或开头的 \/(如 "docs/readme.txt" 合法,"C:\\docs\\readme.txt""/docs/readme.txt" 会出错)
  • 压缩目录时需递归遍历,对子目录也创建一个带结尾 /ZipEntry(如 "images/"),否则解压后目录丢失
  • 写入文件内容前必须先调用 putNextEntry(),且每个 entry 只能写一次;写完要调用 closeEntry()
  • 若源文件含中文名,ZipOutputStream 默认使用 IBM437 编码,Windows 下需改用 ZipOutputStream(OutputStream, Charset.forName("GBK"))(JDK 7+ 支持)
try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream("out.zip"), StandardCharsets.UTF_8)) {
    addFileToZip(zos, Paths.get("src"), "");
} catch (IOException e) {
    e.printStackTrace();
}

void addFileToZip(ZipOutputStream zos, Path file, String prefix) throws IOException {
    String entryName = prefix + file.getFileName().toString();
    if (Files.isDirectory(file)) {
        zos.putNextEntry(new ZipEntry(entryName + "/"));
        zos.closeEntry();
        try (Stream stream = Files.list(file)) {
            stream.forEach(child -> {
                try {
                    addFileToZip(zos, child, entryName + "/");
                } catch (IOException e) {
                    throw new UncheckedIOException(e);
                }
            });
        }
    } else {
        zos.putNextEntry(new ZipEntry(entryName));
        Files.copy(file, zos);
        zos.closeEntry();
    }
}

ZipInputStream 安全解压 ZIP 文件

不能直接按 ZipEntry.getName() 创建 File,否则可能被路径穿越攻击(如 "../etc/passwd");同时要校验条目类型和目标路径合法性:

  • 跳过 isDirectory()true 的条目(目录本身不包含数据,只靠路径名隐式存在)
  • 对每个 entry.getName() 调用 Paths.get(entry.getName()).normalize(),再检查是否仍以目标解压根目录为前缀
  • 禁止解压到系统敏感路径(如 "C:\\""/etc/"),建议统一解压到临时子目录
  • 读取内容时用 ZipInputStream.read(byte[]) 循环,不要一次性 readAllBytes()——大文件会 OOM
Path targetDir = Paths.get("unzipped");
Files.createDirectories(targetDir);

try (ZipInputStream zis = new ZipInputStream(new FileInputStream("in.zip"), StandardCharsets.UTF_8)) {
    ZipEntry entry;
    while ((entry = zis.getNextEntry()) != null) {
        Path targetFile = targetDir.resolve(entry.getName()).normalize();
        // 防穿越:确保 targetFile 仍在 targetDir 下
        if (!targetFile.startsWith(targetDir.toAbsolutePath().normalize())) {
            throw new IOException("Bad zip entry: " + entry.getName());
        }
        if (entry.isDirectory()) {
            Files.createDirectories(targetFile);
        } else {
            Files.createDirectories(targetFile.getParent());
            Files.copy(zis, targetFile, StandardCopyOption.REPLACE_EXISTING);
        }
        zis.closeEntry();
    }
}

处理 ZIP 中的中文文件名乱码问题

根本原因是 ZIP 规范未强制指定编码,不同操作系统/工具默认不同:Windows 通常用 GBK,macOS/Linux 多用 UTF-8。JDK 7+ 的 ZipInputStream/ZipOutputStream 构造函数支持传入 Charset,但旧版 JDK(≤6)或某些 ZIP 工具生成的包仍可能不兼容:

标小兔AI写标书
标小兔AI写标书

一款专业的标书AI代写平台,提供专业AI标书代写服务,安全、稳定、速度快,可满足各类招投标需求,标小兔,写标书,快如兔。

下载

立即学习Java免费学习笔记(深入)”;

  • 若已知 ZIP 由 Windows 上 7-Zip 或 WinRAR 生成,优先试 Charset.forName("GBK")
  • 若不确定,可先用 ZipInputStream 读取条目名,按 UTF-8 解码失败后再用 GBK 尝试(需自己封装 fallback 逻辑)
  • 避免用 FileInputStream + ZipInputStream 组合去“猜”编码——流已消费不可重置,应改用 ByteArrayInputStream 缓存原始字节再多次尝试
  • 真正跨平台稳定的方案是:压缩端统一用 UTF-8(JDK 7+ 设置 StandardCharsets.UTF_8),解压端也用 UTF-8;若必须兼容老 ZIP,则需引入 ant.jarZipFile(支持自动检测)或 TrueZip

为什么不用 java.util.jar

JarOutputStreamJarInputStreamZipOutputStream/ZipInputStream 的子类,仅额外支持 META-INF/MANIFEST.MF。普通压缩解压完全没必要用它:

  • 写 JAR 会强制在 ZIP 根目录加 META-INF/ 目录,哪怕你没提供清单文件
  • 读 JAR 时若 ZIP 不含 META-INF/MANIFEST.MFJarInputStream 会抛 IOException(而 ZipInputStream 不会)
  • 性能无差异,API 几乎一致,但语义混淆——除非你在打包 Java 应用,否则坚持用 zip 相关类

最常被忽略的是:ZIP 条目名编码不是流的字符集,而是 ZIP 文件元数据的编码;ZipInputStream 构造时传的 Charset 只影响 getEntry().getName() 返回值,不影响文件内容解码——内容始终是原始字节流,该用什么编码读文件内容,和 ZIP 编码无关。

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

834

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

739

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

735

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

397

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

399

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

446

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

430

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16926

2023.08.03

高德地图升级方法汇总
高德地图升级方法汇总

本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

27

2026.01.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL 教程
PostgreSQL 教程

共48课时 | 7.3万人学习

Git 教程
Git 教程

共21课时 | 2.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号