You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java提取JAR包时的文件名大小写冲突问题

解决Mac上Shadow JAR解压时的大小写冲突问题

嘿,我遇到过几乎一模一样的问题!核心原因是Mac默认的APFS文件系统是大小写不敏感但保留大小写显示的——这就导致JAR里看似不同的路径(比如license文件和LICENSE/目录),在Mac上会被识别成同一个路径,解压时自然就冲突了。

问题根源

你的Shadow JAR里应该同时存在两种冲突条目:要么是先出现一个叫license的普通文件,之后又出现LICENSE/licence.txt的目录+文件;要么是反过来的顺序。在Mac的文件系统中,LICENSE和license被视为同一个路径,当代码尝试创建目录时,发现这个路径已经被一个文件占用,就会抛出Not a directory的错误——你看到的MANIFEST.MF报错只是冲突的其中一个表现,本质都是大小写导致的路径冲突。

你的代码哪里出问题了?

当前的解压代码有两个关键漏洞:

  • 没判断ZipEntry是否是目录:不管是文件还是目录,你都直接创建FileOutputStream,如果是目录的话肯定会出错;
  • 没处理大小写冲突:针对Mac这类大小写不敏感的文件系统,没有提前检查路径是否被同名(不区分大小写)的文件/目录占用。

修复后的完整代码

我给你修改了解压函数,解决了这些问题,还加了try-with-resources来避免资源泄漏:

public void unZipIt(String zipFile, String outputFolder) {
    byte[] buffer = new byte[1024];
    try {
        // 创建输出目录(用mkdirs确保多级目录都能创建)
        File outputDir = new File(outputFolder);
        if (!outputDir.exists()) {
            outputDir.mkdirs();
        }

        // try-with-resources自动关闭流,避免资源泄漏
        try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile))) {
            ZipEntry entry = zis.getNextEntry();
            while (entry != null) {
                String entryName = entry.getName();
                File targetFile = new File(outputFolder + File.separator + entryName);
                System.out.println("Processing: " + targetFile.getAbsolutePath());

                if (entry.isDirectory()) {
                    // 处理目录:如果存在同名文件,先删除再创建目录
                    if (targetFile.exists()) {
                        if (targetFile.isFile()) {
                            targetFile.delete();
                        }
                    }
                    targetFile.mkdirs();
                } else {
                    // 处理文件:先确保父目录存在,解决大小写冲突
                    File parentDir = targetFile.getParentFile();
                    if (parentDir != null) {
                        if (parentDir.exists()) {
                            if (parentDir.isFile()) {
                                parentDir.delete();
                            }
                        } else {
                            parentDir.mkdirs();
                        }
                    }
                    // 写入文件内容
                    try (FileOutputStream fos = new FileOutputStream(targetFile)) {
                        int readLen;
                        while ((readLen = zis.read(buffer)) > 0) {
                            fos.write(buffer, 0, readLen);
                        }
                    }
                }

                zis.closeEntry();
                entry = zis.getNextEntry();
            }
        }
        System.out.println("解压完成!");
    } catch (IOException ex) {
        ex.printStackTrace();
    }
}

从源头上避免问题的建议

比起解压时修复,更稳妥的是在生成Shadow JAR时就排除冲突的条目。比如在你的build.gradle里配置shadowJar任务,去掉重复的license相关条目:

shadowJar {
    // 排除根目录下的license文件,保留LICENSE目录下的文件
    exclude 'license'
    // 如果有其他大小写冲突的条目,也可以在这里排除
}

这样生成的JAR里就不会有冲突的路径,解压时自然不会出错啦!


内容的提问来源于stack exchange,提问作者Vitali Melamud

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:18:47