Java 在 Mac OS X Catalina - 在资源中找不到 Zip 文件,而在 运行 从 Eclipse 中找到该文件

Java in Mac OS X Catalina - Zip file not found in Resources, while the file is found when run from Eclipse

我在Resources文件夹里有一个压缩文件,第一次解压到程序所在的文件夹里运行。如果程序从 Eclipse 启动,则可以毫无问题地找到并解压缩文件。当我将程序导出到 jar 文件中时,运行 程序带有:

java -jar JRS2020-31.jar

输出为:

java.lang.RuntimeException: Error unzipping file initialData/compressed.zip
    at InterpreteSQL.Main.unzip(Main.java:111)
    at InterpreteSQL.Main.CreateInitialDirectoryIfNotFound(Main.java:77)
    at InterpreteSQL.Main.main(Main.java:60)
Caused by: java.io.FileNotFoundException: file:/Users/XXX/JRS2020-31.jar!/initialData/compressed.zip (No such file or directory)
    at java.util.zip.ZipFile.open(Native Method)
    at java.util.zip.ZipFile.<init>(ZipFile.java:219)
    at java.util.zip.ZipFile.<init>(ZipFile.java:149)
    at java.util.zip.ZipFile.<init>(ZipFile.java:120)
    at InterpreteSQL.Main.unzip(Main.java:88)
    ... 2 more

请注意,资源中的其他文件(帮助 html 文件)会在程序中定期打开。

这是打开文件的代码:

public static void unzip(String zipFilePath, String unzipDir) throws Exception {
    try{
        ZipFile zipFile = new ZipFile(Main.class.getClassLoader().getResource(zipFilePath).getFile());
        Enumeration<? extends ZipEntry> entries = zipFile.entries();
            while(entries.hasMoreElements()){
...

它被调用:

unzip("initialData/compressed.zip", "JRS_directory");

请注意,该程序 运行 在主文件夹中,它可以创建目录和文件。

对这个问题有什么想法吗?非常感谢。

您在两个方面错误地使用了 getResources。

  1. 您无缘无故地要求资源将自己变成一个文件。在您的 eclipse 运行 中,资源 一个文件。但是,在 jar 运行 中,它不是(它是 jar 中的条目,而不是文件)。不要在这些资源上调用 .getFile();抽象的全部意义在于它可能根本不是一个文件。可能是一个罐子里的条目。可以通过网络导入。您所知道的是:从您找到 class 文件的任何地方加载此数据。

不要使用ZipFile;使用 ZipInputStream,它与 API 非常相似。然后使用 getResourceAsStream.

  1. 正确的形式是Main.class.getResource()。避免使用 classloader 中间体。它既是无意义的方法调用,在极少数情况下会中断(在某些情况下,class 的 classloader 为空,导致空指针异常)。请注意,这种形式意味着您传入的字符串是相对于 class 位置的。如果您不想这样,请添加前导斜杠。

当您这样做时,这些是必须关闭的资源,所以让我们使用 try-with-resources 结构来确保即使在遇到异常时也能正确完成。

综合起来:

try (InputStream raw = Main.class.getResourceAsStream("/" + zipFilePath);
     ZipInputStream zip = new ZipInputStream(raw)) {

    for (ZipEntry entry; (entry = zip.getNextEntry()) != null; ) {
        // do something with entry here
    }
}