Мой код запускается внутри JAR файла, скажем, foo.jar, и мне нужно знать, в коде, в какой папке работает foo.jar.
Итак, если foo.jar находится в C:\FOO\
, я хочу получить этот путь независимо от того, что мой текущий рабочий каталог.
Мой код запускается внутри JAR файла, скажем, foo.jar, и мне нужно знать, в коде, в какой папке работает foo.jar.
Итак, если foo.jar находится в C:\FOO\
, я хочу получить этот путь независимо от того, что мой текущий рабочий каталог.
return new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation()
.toURI()).getPath();
Замените "MyClass" на имя вашего класса.
Очевидно, это будет делать странные вещи, если ваш класс был загружен из не файлового местоположения.
Лучшее решение для меня:
String path = Test.class.getProtectionDomain().getCodeSource().getLocation().getPath();
String decodedPath = URLDecoder.decode(path, "UTF-8");
Это должно решить проблему с пробелами и специальными символами.
Чтобы получить File
для данного Class
, есть два шага:
Class
в URL
URL
в File
Важно понимать оба шага и не связывать их.
Как только у вас есть File
, вы можете вызвать getParentFile
, чтобы получить содержащую папку, если это то, что вам нужно.
Class
до URL
Как обсуждалось в других ответах, существует два основных способа найти URL
, относящихся к Class
.
URL url = Bar.class.getProtectionDomain().getCodeSource().getLocation();
URL url = Bar.class.getResource(Bar.class.getSimpleName() + ".class");
У обоих есть плюсы и минусы.
Подход getProtectionDomain
дает базовое расположение класса (например, содержащий JAR файл). Однако, возможно, что политика безопасности среды выполнения Java будет бросать SecurityException
при вызове getProtectionDomain()
, поэтому, если ваше приложение должно запускаться в различных средах, лучше всего протестировать их во всех них.
Подход getResource
дает полный путь ресурса URL класса, из которого вам потребуется выполнить дополнительные манипуляции с строками. Это может быть путь file:
, но также может быть jar:file:
или даже что-то более неприятное, чем bundleresource://346.fwk2106232034:4/foo/Bar.class
при выполнении в рамках OSGi. И наоборот, подход getProtectionDomain
корректно дает URL file:
даже изнутри OSGi.
Обратите внимание, что оба теста getResource("")
и getResource(".")
не выполнялись в моих тестах, когда класс находился в JAR файле; оба вызова возвращены null. Поэтому я рекомендую вместо # 2 вызов, показанный выше, так как кажется более безопасным.
URL
до File
В любом случае, если у вас есть URL
, следующий шаг преобразуется в File
. Это его собственная задача; см. сообщение в блоге Kohsuke Kawaguchi об этом, но, в общем, вы можете использовать new File(url.toURI())
, пока URL-адрес полностью сформирован.
Наконец, я бы очень не приветствовал использование URLDecoder
. Некоторые символы URL, :
и /
в частности, не являются допустимыми символами в кодировке URL. Из URLDecoder Javadoc:
Предполагается, что все символы в кодированной строке являются одним из следующих: "a" через "z", "A" через "Z", "0" - "9" и "-", "_", ".", а также "*". Символ "%" разрешен, но интерпретируется как начало специальной escape-последовательности.
...
Есть два возможных способа, которыми этот декодер может иметь дело с незаконными строками. Он может либо оставить незаконные символы в одиночку, либо он может вызвать исключение 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 path from the URL string,
// leaving the base path.
// get the class 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 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 Common:
Вы также можете использовать:
CodeSource codeSource = YourMainClass.class.getProtectionDomain().getCodeSource();
File jarFile = new File(codeSource.getLocation().toURI().getPath());
String jarDir = jarFile.getParentFile().getPath();
Используйте ClassLoader.getResource(), чтобы найти URL для вашего текущего класса.
Например:
package foo;
public class Test
{
public static void main(String[] args)
{
ClassLoader loader = Test.class.getClassLoader();
System.out.println(loader.getResource("foo/Test.class"));
}
}
(Этот пример взят из аналогичного вопроса.)
Чтобы найти каталог, вам необходимо вручную удалить URL. См. учебник JarClassLoader для формата URL-адреса jar.
Я удивлен, увидев, что никто в последнее время не предложил использовать Path
. Ниже приводится цитата: "Класс Path
включает в себя различные методы, которые могут использоваться для получения информации о пути, элементах доступа к пути, преобразования пути к другим формам или извлечения частей пути"
Таким образом, хорошей альтернативой является получение объекта Path
как:
Path path = Paths.get(Test.class.getProtectionDomain().getCodeSource().getLocation().toURI());
Единственное решение, которое работает для меня в 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();
}
Здесь обновляется до других комментариев, которые мне кажутся неполными по спецификациям
используя относительную "папку" вне файла .jar(в банке такой же место нахождения):
String path =
YourMainClassName.class.getProtectionDomain().
getCodeSource().getLocation().getPath();
path =
URLDecoder.decode(
path,
"UTF-8");
BufferedImage img =
ImageIO.read(
new File((
new File(path).getParentFile().getPath()) +
File.separator +
"folder" +
File.separator +
"yourfile.jpg"));
У меня была та же проблема, и я решил так:
File currentJavaJarFile = new File(Main.class.getProtectionDomain().getCodeSource().getLocation().getPath());
String currentJavaJarFilePath = currentJavaJarFile.getAbsolutePath();
String currentRootDirectoryPath = currentJavaJarFilePath.replace(currentJavaJarFile.getName(), "");
Надеюсь, я помог вам.
Для получения пути запуска jar файла я изучил вышеупомянутые решения и пробовал все методы, которые существуют, как разница между собой. Если этот код работает в Eclipse IDE, все они должны иметь возможность найти путь к файлу, включая указанный класс, и открыть или создать указанный файл с найденным путем.
Но это сложно, когда вы запускаете исполняемый файл jar напрямую или через командную строку, он будет сбой, так как путь файла jar, полученный из указанных выше методов, даст внутренний путь в файле jar, то есть всегда дает путь как
rsrc: имя проекта (возможно, я должен сказать, что это имя пакета основного файла класса - указанный класс)
Я не могу преобразовать путь rsrc:... к внешнему пути, то есть при запуске файла jar за пределами Eclipse IDE он не может получить путь к файлу jar.
Единственный возможный способ получить путь к запуску файла jar вне Eclipse IDE -
System.getProperty("java.class.path")
эта строка кода может возвращать живой путь (включая имя файла) работающего файла jar (обратите внимание, что путь возврата не является рабочим каталогом), так как java-документ и некоторые люди говорят, что он вернет пути все файлы классов в том же каталоге, но поскольку мои тесты, если в том же каталоге содержат много файлов jar, он возвращает только путь запуска jar (о проблемах с несколькими путями, действительно, это произошло в Eclipse).
выбранный ответ выше не работает, если вы запустите банку, щелкнув по ней из среды рабочего стола Gnome (не из любого script или терминала).
Вместо этого я увлекаюсь тем, что во всем мире работает следующее решение:
try {
return URLDecoder.decode(ClassLoader.getSystemClassLoader().getResource(".").getPath(), "UTF-8");
} catch (UnsupportedEncodingException e) {
return "";
}
На самом деле, это лучшая версия - старая неудача, если в названии папки было пробел.
private String getJarFolder() {
// get name and path
String name = getClass().getName().replace('.', '/');
name = getClass().getResource("/" + name + ".class").toString();
// remove junk
name = name.substring(0, name.indexOf(".jar"));
name = name.substring(name.lastIndexOf(':')-1, name.lastIndexOf('/')+1).replace('%', ' ');
// remove escape characters
String s = "";
for (int k=0; k<name.length(); k++) {
s += name.charAt(k);
if (name.charAt(k) == ' ') k += 2;
}
// replace '/' with system separator char
return s.replace('/', File.separatorChar);
}
Что касается отказа от апплетов, вы обычно не имеете доступа к локальным файлам. Я не знаю много о JWS, но для обработки локальных файлов может не получиться скачать приложение.?
Самое простое решение - передать путь в качестве аргумента при запуске jar.
Вы можете автоматизировать это с помощью оболочки script (.bat в Windows,.sh в другом месте):
java -jar my-jar.jar .
Я использовал .
для передачи текущего рабочего каталога.
UPDATE
Возможно, вы захотите вставить файл jar в подкаталог, чтобы пользователи не случайно его нажимали. Ваш код также должен проверить, чтобы убедиться, что аргументы командной строки были предоставлены, и предоставить хорошее сообщение об ошибке, если аргументы отсутствуют.
Другие ответы, похоже, указывают на источник кода, который является расположением файла Jar, который не является каталогом.
Использование
return new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getParentFile();
Мне пришлось много возиться до того, как я наконец нашел рабочее (и короткое) решение.
Возможно, что jarLocation
поставляется с префиксом типа file:\
или jar:file\
, который можно удалить с помощью String#substring()
.
URL jarLocationUrl = MyClass.class.getProtectionDomain().getCodeSource().getLocation();
String jarLocation = new File(jarLocationUrl.toString()).getParent();
String path = getClass().getResource("").getPath();
Путь всегда ссылается на ресурс в файле jar.
public static String dir() throws URISyntaxException
{
URI path=Main.class.getProtectionDomain().getCodeSource().getLocation().toURI();
String name= Main.class.getPackage().getName()+".jar";
String path2 = path.getRawPath();
path2=path2.substring(1);
if (path2.contains(".jar"))
{
path2=path2.replace(name, "");
}
return path2;}
Хорошо работает в Windows
Я попытался получить пробег в jar, используя
String folder = MyClassName.class.getProtectionDomain().getCodeSource().getLocation().getPath();
c:\app > java -jar application.jar
Запуск приложения jar с именем "application.jar", в Windows в папке "c:\app" значение переменной "String" было "\ c:\app\application.jar", и у меня было тестирование проблем для правильности пути
File test = new File(folder);
if(file.isDirectory() && file.canRead()) { //always false }
Итак, я попытался определить "test" как:
String fold= new File(folder).getParentFile().getPath()
File test = new File(fold);
чтобы получить путь в правильном формате, например, "c:\app" вместо "\ c:\app\application.jar", и я заметил, что он работает.
Что-то, что разочаровывает, заключается в том, что при разработке в Eclipse MyClass.class.getProtectionDomain().getCodeSource().getLocation()
возвращает каталог /bin
, который является большим, но когда вы его компилируете в банку, путь включает в себя часть /myjarname.jar
, которая дает вам незаконный файл имена.
Чтобы код работал как в ide, так и после его компиляции в банку, я использую следующий фрагмент кода:
URL applicationRootPathURL = getClass().getProtectionDomain().getCodeSource().getLocation();
File applicationRootPath = new File(applicationRootPathURL.getPath());
File myFile;
if(applicationRootPath.isDirectory()){
myFile = new File(applicationRootPath, "filename");
}
else{
myFile = new File(applicationRootPath.getParentFile(), "filename");
}
Не совсем уверен в других, но в моем случае он не работал с "Runnable jar", и я получил его, исправляя коды вместе с ответом 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 был экспортирован с помощью "Упаковки внешних библиотек" в Eclipse. По какой-то причине все решения, основанные на ProtectionDomain, в этом случае приводят к нулю.
Комбинируя некоторые решения выше, мне удалось получить следующий рабочий код:
String surroundingJar = null;
// gets the path to the jar file if it exists; or the "bin" directory if calling from Eclipse
String jarDir = new File(ClassLoader.getSystemClassLoader().getResource(".").getPath()).getAbsolutePath();
// gets the "bin" directory if calling from eclipse or the name of the .jar file alone (without its path)
String jarFileFromSys = System.getProperty("java.class.path").split(";")[0];
// If both are equal that means it is running from an IDE like Eclipse
if (jarFileFromSys.equals(jarDir))
{
System.out.println("RUNNING FROM IDE!");
// The path to the jar is the "bin" directory in that case because there is no actual .jar file.
surroundingJar = jarDir;
}
else
{
// Combining the path and the name of the .jar file to achieve the final result
surroundingJar = jarDir + jarFileFromSys.substring(1);
}
System.out.println("JAR File: " + surroundingJar);
Этот метод, вызванный из кода в архиве, возвращает папку, в которой находится файл .jar. Он должен работать либо в Windows, либо в Unix.
private String getJarFolder() {
String name = this.getClass().getName().replace('.', '/');
String s = this.getClass().getResource("/" + name + ".class").toString();
s = s.replace('/', File.separatorChar);
s = s.substring(0, s.indexOf(".jar")+4);
s = s.substring(s.lastIndexOf(':')-1);
return s.substring(0, s.lastIndexOf(File.separatorChar)+1);
}
Производится из кода по адресу: Определить, работает ли из JAR
Упомяните, что он проверяется только в Windows
, но я думаю, что он отлично работает на других операционных системах [Linux,MacOs,Solaris
]:).
У меня были файлы 2 .jar
в том же каталоге. Я хотел от одного файла .jar
запустить другой файл .jar
, который находится в том же каталоге.
Проблема заключается в том, что при запуске из cmd
текущий каталог system32
.
предупреждения!
;][[;'57f2g34g87-8+9-09!2#@!$%^^&()
или ()%&$%^@#
он работает хорошо.ProcessBuilder
следующим образом:🍂..
//The class from which i called this was the class `Main`
String path = getBasePathForClass(Main.class);
String applicationPath= new File(path + "application.jar").getAbsolutePath();
System.out.println("Directory Path is : "+applicationPath);
//Your know try catch here
//Mention that sometimes it doesn't work for example with folder `;][[;'57f2g34g87-8+9-09!2#@!$%^^&()`
ProcessBuilder builder = new ProcessBuilder("java", "-jar", applicationPath);
builder.redirectErrorStream(true);
Process process = builder.start();
//...code
🍂 getBasePathForClass(Class<?> classs)
:
/**
* Returns the absolute path of the current directory in which the given
* class
* file is.
*
* @param classs
* @return The absolute path of the current directory in which the class
* file is.
* @author GOXR3PLUS[StackOverFlow user] + bachden [StackOverFlow user]
*/
public static final String getBasePathForClass(Class<?> classs) {
// Local variables
File file;
String basePath = "";
boolean failed = false;
// Let give a first try
try {
file = new File(classs.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
if (file.isFile() || file.getPath().endsWith(".jar") || file.getPath().endsWith(".zip")) {
basePath = file.getParent();
} else {
basePath = file.getPath();
}
} catch (URISyntaxException ex) {
failed = true;
Logger.getLogger(classs.getName()).log(Level.WARNING,
"Cannot firgue out base path for class with way (1): ", ex);
}
// The above failed?
if (failed) {
try {
file = new File(classs.getClassLoader().getResource("").toURI().getPath());
basePath = file.getAbsolutePath();
// the below is for testing purposes...
// starts with File.separator?
// String l = local.replaceFirst("[" + File.separator +
// "/\\\\]", "")
} catch (URISyntaxException ex) {
Logger.getLogger(classs.getName()).log(Level.WARNING,
"Cannot firgue out base path for class with way (2): ", ex);
}
}
// fix to run inside eclipse
if (basePath.endsWith(File.separator + "lib") || basePath.endsWith(File.separator + "bin")
|| basePath.endsWith("bin" + File.separator) || basePath.endsWith("lib" + File.separator)) {
basePath = basePath.substring(0, basePath.length() - 4);
}
// fix to run inside netbeans
if (basePath.endsWith(File.separator + "build" + File.separator + "classes")) {
basePath = basePath.substring(0, basePath.length() - 14);
}
// end fix
if (!basePath.endsWith(File.separator)) {
basePath = basePath + File.separator;
}
return basePath;
}
Этот код работал у меня:
private static String getJarPath() throws IOException, URISyntaxException {
File f = new File(LicensingApp.class.getProtectionDomain().().getLocation().toURI());
String jarPath = f.getCanonicalPath().toString();
String jarDir = jarPath.substring( 0, jarPath.lastIndexOf( File.separator ));
return jarDir;
}
Если баночка работает, то путь к банке можно получить с помощью следующего кода:
public class JarpathTest
{
public static void main(String[] args)
{
File jarDir = new File(ClassLoader.getSystemClassLoader().getResource(".").getPath());
System.out.println(jarDir.getAbsolutePath());
}
}
Игнорировать ответ резервного помощника, он может выглядеть нормально, но имеет несколько проблем:
здесь оба должны быть +1 не -1:
name = name.substring(name.lastIndexOf(':')-1, name.lastIndexOf('/')+1).replace('%', ' ');
Очень опасно, потому что не сразу видно, если путь не имеет пробелов, но замена только "%" оставит вас с пучком по 20 в каждом пробеле:
name = name.substring(name.lastIndexOf(':')-1, name.lastIndexOf('/')+1).replace('%', ' ');
Есть более эффективные пути, чем этот цикл для пробелов.
Также это вызовет проблемы во время отладки.
Я пишу в Java 7 и тестирую в Windows 7 с исполняемой средой Oracle, а Ubuntu - с открытым исходным кодом. Это идеально подходит для этих систем:
Путь к родительскому каталогу любого запущенного файла jar (если класс, вызывающий этот код, является прямым дочерним элементом самого архива jar):
try {
fooDir = new File(this.getClass().getClassLoader().getResource("").toURI());
} catch (URISyntaxException e) {
//may be sloppy, but don't really need anything here
}
fooDirPath = fooDir.toString(); // converts abstract (absolute) path to a String
Итак, путь foo.jar будет следующим:
fooPath = fooDirPath + File.separator + "foo.jar";
Опять же, это не было протестировано ни на Mac, ни на старой Windows
Подход getProtectionDomain
может не работать, например, например. когда вам нужно найти банку для некоторых основных классов Java (например, в моем случае StringBuilder
класс в IBM JDK), однако следуйте за планами:
public static void main(String[] args) {
System.out.println(findSource(MyClass.class));
// OR
System.out.println(findSource(String.class));
}
public static String findSource(Class<?> clazz) {
String resourceToSearch = '/' + clazz.getName().replace(".", "/") + ".class";
java.net.URL location = clazz.getResource(resourceToSearch);
String sourcePath = location.getPath();
// Optional, Remove junk
return sourcePath.replace("file:", "").replace("!" + resourceToSearch, "");
}
У меня есть другой способ получить расположение String класса.
URL path = Thread.currentThread().getContextClassLoader().getResource("");
Path p = Paths.get(path.toURI());
String location = p.toString();
Строка вывода будет иметь вид
C:\Users\Administrator\new Workspace\...
Пространства и другие символы обрабатываются и в форме без file:/
. Так будет проще в использовании.
Или вы можете передать текущий поток таким образом:
String myPath = Thread.currentThread().getContextClassLoader().getResource("filename").getPath();