netbeans 运行jar时的当前目录路径

dgsult0t  于 2023-02-04  发布在  其他
关注(0)|答案(3)|浏览(140)

我的程序通过从当前目录阅读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的情况下运行时,如何让程序加载当前目录中的文件?
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
zzlelutf

zzlelutf1#

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

shell

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代码借用自this stackoverflow answer
然后,您可以将jar移动到任何您想要的地方,甚至将其发送给您的用户,而不必担心配置文件在哪里。

j1dl9f46

j1dl9f462#

正如维克托已经指出的,当前目录依赖于启动JVM的命令,因此在运行时是动态的,而需要一个依赖于jar文件本身位置的定位器,这意味着它在编译时是动态的,但在运行时是静态的。
这里有不同的方法,所以让我简短地介绍两种:

使用启动程序脚本

这样你就可以自己控制命令行了,但是你必须在你计划使用程序的每个操作系统上都这样做。在Windows上,它可能是这样的:
app.bat:

cd %~dp0
java -jar app.jar

更多信息请参见第一行here

使用系统类加载器

这是可行的,因为System ClassLoader的源代码在编译时是动态的,而在运行时是静态的,这正是您所需要的,但是,它也有缺点,您不能写入配置文件,因为您只能得到一个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();
        }
    }
}

ConfigFile.txt

Hello World

MANIFEST.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
eit6fx6z

eit6fx6z3#

正如我所看到的,这个问题的出现是因为您需要知道 * 相对路径是如何在执行时解析的 *,而"当前目录"发生在这个等式上,所以请允许我稍微解释一下。
据我所知,"当前目录"也称为工作目录,它是在您启动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如何设置工作目录以及 * 如何使用工作目录作为前缀解析相对路径 *,将帮助您从现在开始解决任何引用问题。
您可能会发现此链接很有用:How to get the current working directory in Java?,用于调查NetBeans(为我们)启动Java应用程序时设置的工作目录。

相关问题