【问题标题】:Current directory path when run jar运行jar时的当前目录路径
【发布时间】:2019-09-20 02:35:53
【问题描述】:

我的程序通过从当前目录读取xml文件来读取配置数据:

File fXmlFile = new File("configFile.xml");

它在 NetBeans IDE 中运行良好。我已经构建项目并获得了 jar 文件。如果我在 Windows 10 中双击它,它运行良好。如果我通过右键单击 jar 打开文件并且Open with -> Java 程序找不到配置文件。在这种情况下,我遇到了异常:

java.io.FileNotFoundException: C:\Windows\System32\configFile.xml (The system cannot find the file specified)

为什么它只查看系统路径而不是当前目录?在Open with -> Java case下运行时如何要求程序加载当前目录下的文件?

Jar 的清单文件:

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.10.4
Created-By: 12.0.1+12 (Oracle Corporation)
Class-Path: lib/log4j-api-2.11.2.jar lib/log4j-core-2.11.2.jar lib/met
 ouia.jar lib/swt.jar
X-COMMENT: Main-Class will be added automatically by build
Main-Class: com.aaa.myprog.runMe 

【问题讨论】:

  • 配置文件驻留在哪里,相对于你的 jar 文件?
  • jar和配置文件在同一个目录

标签: java netbeans


【解决方案1】:

阅读config.xml 和您的应用可能需要的其他资产的最佳方法是将它们放在src/main/resources 中,然后将它们作为文件引用到您的类路径中,如下所示:

外壳

mv configFile.xml /users/vico/my_program/src/main/resources

Java 代码

// ...

public static File getResourceAsFile(String resourcePath) {
    try {
        InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(resourcePath);
        if (in == null) {
            return null;
        }

        File tempFile = File.createTempFile(String.valueOf(in.hashCode()), ".tmp");
        tempFile.deleteOnExit();

        try (FileOutputStream out = new FileOutputStream(tempFile)) {
            //copy stream
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, bytesRead);
            }
        }
        return tempFile;
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
}

File fXmlFile = getResourceAsFile("configFile.xml");

// ...

(此代码借自this stackoverflow answer

然后,您可以将您的 jar 移动到任何您想要的位置,甚至将其发送给您的用户,而不必担心配置文件的位置。

【讨论】:

    【解决方案2】:

    正如 Victor 已经指出的,当前目录依赖于用于启动 JVM 的命令,因此在运行时是动态的。相反,您需要一个依赖于 jar 文件本身位置的定位器,这意味着它在编译时是动态的,但在运行时是静态的。

    这里有不同的方法,所以我简单介绍两种:

    使用启动器脚本
    这样,您只需自己控制命令行,但您必须为您计划使用程序的每个操作系统执行此操作。在 Windows 上,它可能如下所示:

    app.bat:

    cd %~dp0
    java -jar app.jar
    

    更多信息见第一行here

    使用系统类加载器
    这是可行的,因为系统类加载器的源在编译时是动态的,但在运行时是静态的,所以正是您需要的。但是,它的缺点是您无法写入配置文件,因为您只能获得InputStream

    app.jar

    try (InputStream fXml = ClassLoader.getSystemClassLoader().getResourceAsStream("configFile.xml")) {
        ...
    }
    

    还有一个完整的 MCVE。

    ConfigFile.java:

    public class ConfigFile {
        public static void main(String[] args) {
            try (final BufferedReader configFile = new BufferedReader(
                new InputStreamReader(ClassLoader.getSystemClassLoader()
                    .getResourceAsStream("configFile.txt")))) {
                System.out.println(configFile.readLine());
            } catch (final IOException exc) {
                exc.printStackTrace();
            }
        }
    }
    

    配置文件.txt

    Hello World
    

    清单.MF

    Manifest-Version: 1.0
    Class-Path: .
    Main-Class: prv.izruo.test.ConfigFile
    

    命令行

    P:\workspace\ConfigFile>dir deploy
    ...
    02.05.2019  20:43             1.434 configFile.jar
    02.05.2019  20:43                11 configFile.txt
    
    P:\workspace\ConfigFile>java -jar deploy\configFile.jar
    Hello World
    

    【讨论】:

    • 我不喜欢使用任何启动脚本我选择第二种方法。但我在 fXml 中得到了 null
    • 如果我将configFile.xml 放入 jar 文件中,一切都会正常。如何要求系统不是从jar中获取资源,而是从jar文件所在的同一目录获取资源?
    • 老实说,我不知道。今天我在工作中重新检查了我的项目,并在家里编写了一个 MCVE。两者都很好。
    • @vico 我想我发现它正在研究我的 MCVE 清单:您必须将 . 添加到您的类路径(或您希望将配置文件保存到的任何文件夹)。
    【解决方案3】:

    我看到问题的出现是因为您需要知道 在执行时如何解决相对路径,并且“当前目录”发生在这个 ecuation 上,所以请允许我稍微解释一下.

    据我所知,“当前目录”也称为 工作目录,它是在您启动 java 应用程序时设置的,它假定 您执行的目录的值启动应用程序的 java 命令

    例如:

    如果你打开一个命令终端,你把自己放在一个目录"C:\pepe" 中,然后从那个目录中执行命令来启动你的jar,例如:java -jar "c:\path\to\my\app.jar" 那么工作目录就是"c:\pepe\" 并且在里面您的程序对相对路径的任何引用都将使用 "c:\pepe\" 前缀完成。

    File fXmlFile = new File("configFile.xml"); 行将指示 jvm 在"c:\pepe\configFile.xml" 处查看文件,因为相对路径configFile.xml 将使用工作目录(之前在启动时设置)解析,如上所述。

    牢记java如何设置工作目录以及如何使用工作目录作为前缀解析相对路径,帮助您解决任何参考问题,从现在开始!

    您可能会发现此链接很有用Getting the Current Working Directory in Java 用于调查 netbeans 在启动(对我们而言)java 应用程序时设置的工作目录。

    TD;DR:

    “pepe”是西班牙语,相当于英语中的“foo”:D,希望能学到新东西! :D

    【讨论】:

    • “如果你执行c:\pepe\java -jar "c:\path\to\my\app.jar",工作目录将是"c:\pepe\"”——这是不正确的。您可以使用cd 将任何目录设为当前工作目录,然后执行相同的命令。工作目录不是由执行程序的位置定义的,它是单个进程或父命令 shell 的属性。
    • 感谢您指出这一点,据我所知,在没有任何附加配置的情况下,您的工作目录默认为您执行命令的目录。如果还有其他设置方法,请启发我们,随时编辑我的帖子,以列举其他设置工作目录的方法。
    • cd C:\pepe 是您更改工作目录的方式。
    • 啊哈明白你在说什么,请允许我修复我的帖子
    • @VGR 感谢您的建议,我已编辑帖子;你还有其他观察吗?再次:随时编辑我的帖子
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-22
    • 1970-01-01
    • 2019-01-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多