我的代码在一个JAR文件中运行,比如foo.jar,我需要知道,在代码中,运行的foo.jar在哪个文件夹中。
所以,如果FOO .jar在C:\FOO\中,无论我当前的工作目录是什么,我都想获得这个路径。
我的代码在一个JAR文件中运行,比如foo.jar,我需要知道,在代码中,运行的foo.jar在哪个文件夹中。
所以,如果FOO .jar在C:\FOO\中,无论我当前的工作目录是什么,我都想获得这个路径。
当前回答
上述方法在我的Spring环境中并不适用,因为Spring将实际的类隐藏到一个名为BOOT-INF的包中,因此不是运行文件的实际位置。我发现了另一种方法来检索运行文件通过权限对象已授予运行文件:
public static Path getEnclosingDirectory() {
return Paths.get(FileUtils.class.getProtectionDomain().getPermissions()
.elements().nextElement().getName()).getParent();
}
其他回答
对我来说,在Linux、Mac和Windows上都适用的唯一解决方案是:
public static String getJarContainingFolder(Class aclass) throws Exception {
CodeSource codeSource = aclass.getProtectionDomain().getCodeSource();
File jarFile;
if (codeSource.getLocation() != null) {
jarFile = new File(codeSource.getLocation().toURI());
}
else {
String path = aclass.getResource(aclass.getSimpleName() + ".class").getPath();
String jarFilePath = path.substring(path.indexOf(":") + 1, path.indexOf("!"));
jarFilePath = URLDecoder.decode(jarFilePath, "UTF-8");
jarFile = new File(jarFilePath);
}
return jarFile.getParentFile().getAbsolutePath();
}
要获得给定类的文件,有两个步骤:
将类转换为URL 将URL转换为文件
重要的是要理解这两个步骤,而不是将它们混为一谈。
有了文件之后,如果需要的话,可以调用getParentFile来获取包含该文件的文件夹。
步骤1:类到URL
正如在其他答案中讨论的那样,有两种主要方法来查找与类相关的URL。
URL URL = Bar.class.getProtectionDomain().getCodeSource().getLocation(); URL URL = Bar.class.getResource(Bar.class.getSimpleName() + ".class");
两者都有利弊。
getProtectionDomain方法产生类的基本位置(例如,包含JAR文件)。但是,Java运行时的安全策略在调用getProtectionDomain()时可能会抛出SecurityException,因此,如果应用程序需要在各种环境中运行,最好在所有环境中进行测试。
getResource方法生成类的完整URL资源路径,您需要从该路径执行额外的字符串操作。它可以是file: path,但也可以是jar:file:,甚至在OSGi框架中执行时,可以是bundleresource://346.fwk2106232034:4/foo/Bar.class。相反,getProtectionDomain方法甚至可以从OSGi内部正确地生成一个file: URL。
注意,当类驻留在JAR文件中时,getResource("")和getResource(".")在我的测试中都失败了;两次调用都返回null。因此,我推荐使用上面所示的#2调用,因为它似乎更安全。
步骤2:URL到文件
无论哪种方式,一旦您有一个URL,下一步是转换为一个文件。这是它自身的挑战;有关详细信息,请参阅Kohsuke Kawaguchi的博客文章,但简而言之,只要URL完全格式良好,您就可以使用new File(URL . touri())。
最后,我强烈反对使用URLDecoder。URL的某些字符,特别是:和/,不是有效的URL编码字符。从URLDecoder Javadoc:
假设编码字符串中的所有字符都是以下字符之一:"a"到"z", "a"到"z", "0"到"9",以及"-","_","."和"*"。字符“%”是允许的,但被解释为特殊转义序列的开始。 ... 这个解码器有两种可能的方法来处理非法字符串。它可以不处理非法字符,也可以抛出一个IllegalArgumentException异常。解码器采用哪种方法取决于实现。
在实践中,URLDecoder通常不会像上面威胁的那样抛出IllegalArgumentException。如果文件路径中有编码为%20的空格,则此方法似乎可以工作。然而,如果你的文件路径有其他非字母字符,如+,你将有问题与URLDecoder破坏你的文件路径。
工作代码
为了实现这些步骤,你可以使用如下方法:
/**
* Gets the base location of the given class.
* <p>
* If the class is directly on the file system (e.g.,
* "/path/to/my/package/MyClass.class") then it will return the base directory
* (e.g., "file:/path/to").
* </p>
* <p>
* If the class is within a JAR file (e.g.,
* "/path/to/my-jar.jar!/my/package/MyClass.class") then it will return the
* path to the JAR (e.g., "file:/path/to/my-jar.jar").
* </p>
*
* @param c The class whose location is desired.
* @see FileUtils#urlToFile(URL) to convert the result to a {@link File}.
*/
public static URL getLocation(final Class<?> c) {
if (c == null) return null; // could not load the class
// try the easy way first
try {
final URL codeSourceLocation =
c.getProtectionDomain().getCodeSource().getLocation();
if (codeSourceLocation != null) return codeSourceLocation;
}
catch (final SecurityException e) {
// NB: Cannot access protection domain.
}
catch (final NullPointerException e) {
// NB: Protection domain or code source is null.
}
// NB: The easy way failed, so we try the hard way. We ask for the class
// itself as a resource, then strip the class's path from the URL string,
// leaving the base path.
// get the class's raw resource path
final URL classResource = c.getResource(c.getSimpleName() + ".class");
if (classResource == null) return null; // cannot find class resource
final String url = classResource.toString();
final String suffix = c.getCanonicalName().replace('.', '/') + ".class";
if (!url.endsWith(suffix)) return null; // weird URL
// strip the class's path from the URL string
final String base = url.substring(0, url.length() - suffix.length());
String path = base;
// remove the "jar:" prefix and "!/" suffix, if present
if (path.startsWith("jar:")) path = path.substring(4, path.length() - 2);
try {
return new URL(path);
}
catch (final MalformedURLException e) {
e.printStackTrace();
return null;
}
}
/**
* Converts the given {@link URL} to its corresponding {@link File}.
* <p>
* This method is similar to calling {@code new File(url.toURI())} except that
* it also handles "jar:file:" URLs, returning the path to the JAR file.
* </p>
*
* @param url The URL to convert.
* @return A file path suitable for use with e.g. {@link FileInputStream}
* @throws IllegalArgumentException if the URL does not correspond to a file.
*/
public static File urlToFile(final URL url) {
return url == null ? null : urlToFile(url.toString());
}
/**
* Converts the given URL string to its corresponding {@link File}.
*
* @param url The URL to convert.
* @return A file path suitable for use with e.g. {@link FileInputStream}
* @throws IllegalArgumentException if the URL does not correspond to a file.
*/
public static File urlToFile(final String url) {
String path = url;
if (path.startsWith("jar:")) {
// remove "jar:" prefix and "!/" suffix
final int index = path.indexOf("!/");
path = path.substring(4, index);
}
try {
if (PlatformUtils.isWindows() && path.matches("file:[A-Za-z]:.*")) {
path = "file:/" + path.substring(5);
}
return new File(new URL(path).toURI());
}
catch (final MalformedURLException e) {
// NB: URL is not completely well-formed.
}
catch (final URISyntaxException e) {
// NB: URL is not completely well-formed.
}
if (path.startsWith("file:")) {
// pass through the URL as-is, minus "file:" prefix
path = path.substring(5);
return new File(path);
}
throw new IllegalArgumentException("Invalid URL: " + url);
}
你可以在SciJava公共库中找到这些方法:
org.scijava.util.ClassUtils org.scijava.util.FileUtils。
不太确定其他人,但在我的情况下,它不与“可运行的罐子”,我得到了它的工作通过修复代码一起从phchen2答案和另一个从这个链接:如何获得一个运行的jar文件的路径? 代码:
String path=new java.io.File(Server.class.getProtectionDomain()
.getCodeSource()
.getLocation()
.getPath())
.getAbsolutePath();
path=path.substring(0, path.lastIndexOf("."));
path=path+System.getProperty("java.class.path");
最简单的解决方案是在运行jar时将路径作为参数传递。
你可以用一个shell脚本(Windows中的.bat,其他任何地方的.sh)自动化这个:
java -jar my-jar.jar .
我用过。传递当前工作目录。
更新
您可能希望将jar文件放在子目录中,这样用户就不会意外地单击它。您的代码还应该检查以确保提供了命令行参数,如果缺少参数,则提供良好的错误消息。
在我最终找到一个有效(且简短)的解决方案之前,我不得不浪费很多时间。 有可能jarLocation带有一个像file:\或jar:file\这样的前缀,可以使用String#substring()来删除。
URL jarLocationUrl = MyClass.class.getProtectionDomain().getCodeSource().getLocation();
String jarLocation = new File(jarLocationUrl.toString()).getParent();