简体   繁体   English

运行jar时的当前目录路径

[英]Current directory path when run jar

My program read configuration data by reading xml file fro current directory: 我的程序通过从当前目录读取xml文件来读取配置数据:

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

It works fine in NetBeans IDE. 在NetBeans IDE中运行正常。 I have build project and got jar file. 我已经建立了项目并得到了jar文件。 It runs fine if I double click on it in Windows 10. In case I open file by using right click on jar and Open with -> Java program can't find configuration file. 如果我在Windows 10中双击它,它将运行良好。如果我通过在jar上右键单击来打开文件,并且Open with -> Java程序Open with -> Java则找不到配置文件。 In this case I got exception: 在这种情况下,我有例外:

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

Why it looks just to system path and not in current directory? 为什么它看起来只是系统路径而不是当前目录? How to ask program to load file in current directory when running in Open with -> Java case? Open with -> Java case中运行时,如何要求程序将文件加载到当前目录中?

Jar's manifest file: 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 

The best way for you to read config.xml and other assets your app may need is putting them in src/main/resources and then referencing them as files in your classpath, like this: 读取config.xml和应用程序可能需要的其他资产的最佳方法是将它们放在src/main/resources ,然后将它们作为类路径中的文件引用,如下所示:

Shell 贝壳

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

Java Code 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 code was borrowed from this stackoverflow answer ) (此代码是从此stackoverflow答案借来的)

Then you can move your jar wherever you want, even ship it to your users and stop worrying about where the config files lie. 然后,您可以将jar移至所需的任何位置,甚至可以将其运送给用户,而不必担心配置文件的位置。

The current directory is, as Victor already pointed out, dependent on the command that is used to launch the JVM and therefore dynamic at runtime. 正如Victor已经指出的那样,当前目录取决于用于启动JVM的命令,因此在运行时是动态的。 You instead need a locator that is dependent on the location on the jar file itself, meaning it is dynamic at compile time but static at runtime. 相反,您需要一个依赖于jar文件本身位置的定位器,这意味着它在编译时是动态的,而在运行时是静态的。

There are different approaches here, so let me shortly introduce two: 这里有不同的方法,所以让我简短介绍两种:

Use a launcher script 使用启动器脚本
This way you simply take control of the command line yourself, but you have to do it for every operating system where you plan to use your program. 这样,您只需要自己控制命令行即可,但是您必须在计划使用程序的每个操作系统上进行控制。 On Windows it could look like this: 在Windows上可能看起来像这样:

app.bat: app.bat:

cd %~dp0
java -jar app.jar

More information on the first line here . 在第一行的更多信息这里

Use System ClassLoader 使用系统类加载器
This works, because the System ClassLoader's sources are dynamic at compile time but static at runtime, so exactly what you need. 之所以可行,是因为System ClassLoader的源在编译时是动态的,而在运行时是静态的,因此正是您所需要的。 However, it comes with the downside that you cannot write to the configuration file, as you only get an InputStream . 但是,这样做的缺点是,您只能写入InputStream ,因此无法写入配置文件。

app.jar app.jar

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

And a full MCVE. 和完整的MCVE。

ConfigFile.java: 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 ConfigFile.txt

Hello World

MANIFEST.MF 清单文件

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

command line 命令行

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

As i see the problem arises because you need to know how relative paths are resolved at exectution time , and the "current directory" takes place on this ecuation, so allow me to explain a little bit. 正如我所看到的,出现问题是因为您需要知道在执行时如何解决相对路径 ,并且在此执行中会发生“当前目录”,所以请允许我解释一下。

As far i know the "current directory" is also knows as working directory and it is set when you launch a java application and it assumes the value of the directory from where you execute the java command to launch the app . 据我所知,“当前目录”也称为工作目录 ,它是在您启动Java应用程序时设置的,它假定执行Java命令以启动应用程序的目录的值。

So for example: 因此,例如:

If you open a command terminal, and you place yourself in a directory "C:\\pepe" and from whitin that directory you execute the command to launch your jar, eg: java -jar "c:\\path\\to\\my\\app.jar" then the working directory would be "c:\\pepe\\" and inside your program any reference to relative path would be completed using the "c:\\pepe\\" prefix. 如果打开命令终端,并将自己放置在目录"C:\\pepe" ,则从该目录执行命令以启动jar,例如: java -jar "c:\\path\\to\\my\\app.jar"那么工作目录将是"c:\\pepe\\"并且在程序中,对任何相对路径的引用都将使用"c:\\pepe\\"前缀来完成。

And them the line File fXmlFile = new File("configFile.xml"); 然后他们一行File fXmlFile = new File("configFile.xml"); will instruct the jvm to look the file at "c:\\pepe\\configFile.xml" , as the relative path configFile.xml will be resolved using the working directory (set previously at launch-time) as i explained above. 将指示jvm在"c:\\pepe\\configFile.xml"处查找文件,因为相对路径configFile.xml将使用工作目录(在启动时设置)进行解析,如上所述。

Having in mind how java sets the working directory and how relative paths are resolved using the working directory as prefix, help you solve any reference problem, from now onwards! 考虑到java如何设置工作目录以及如何使用工作目录作为前缀来解析相对路径 ,从现在开始,可以帮助您解决任何引用问题!

You may find this link useful : Getting the Current Working Directory in Java for investigating what is the working diretory set by netbeans when it launch (for us) the java app. 您可能会发现此链接很有用在Java中获取当前工作目录,以调查netbeans在启动Java应用程序时为我们设置的工作目录是什么。

TD;DR: TD; DR:

"pepe" is the spanish term equals to "foo" in the english term :D, hope to learn something new!! “ pepe”是西班牙语,等于英语中的“ foo”:D,希望学到新知识! :D :D

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM