[java] 실행중인 JAR 파일의 경로를 얻는 방법?

내 코드는 foo.jar 와 같은 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두 단계가 있습니다.

  1. 변환 ClassA를URL
  2. 변환 URLA를File

두 단계를 이해하고 혼동하지 않는 것이 중요합니다.

일단 당신이 FilegetParentFile 있으면 필요한 경우 포함 폴더를 얻기 위해 전화 를 걸 수 있습니다 .

1 단계: ClassURL

다른 답변에서 논의 된 바와 같이, URL관련성을 찾는 두 가지 주요 방법 이 Class있습니다.

  1. URL url = Bar.class.getProtectionDomain().getCodeSource().getLocation();

  2. URL url = Bar.class.getResource(Bar.class.getSimpleName() + ".class");

둘 다 장단점이 있습니다.

getProtectionDomain접근 방식은 클래스의 기본 위치 (예 : 포함하는 JAR 파일)를 생성합니다. 그러나를 SecurityException호출 할 때 Java 런타임의 보안 정책이 getProtectionDomain()발생할 수 있으므로 응용 프로그램을 다양한 환경에서 실행해야하는 경우 모두 테스트하는 것이 가장 좋습니다.

getResource접근 방식은 클래스의 전체 URL 리소스 경로를 생성하므로 추가 문자열 조작을 수행해야합니다. 그것은있을 수 있습니다 file:경로뿐만 아니라 될 수 jar:file:또는 같은 천하고도 뭔가 bundleresource://346.fwk2106232034:4/foo/Bar.classOSGi 프레임 워크 내에서 실행하는 경우. 반대로,이 getProtectionDomain방법 file:은 OSGi 내에서도 URL을 올바르게 생성합니다 .

모두 참고 getResource("")하고 getResource(".")클래스가 JAR 파일 내에 거주 할 때, 내 테스트에서 실패; 두 호출 모두 null을 반환했습니다. 따라서 위의 # 2 호출이 더 안전 해 보이므로 대신 권장합니다.

2 단계 : URLFile

어느 쪽이든, 일단 URL다음 단계는로 변환됩니다 File. 이것은 그 자체의 도전이다. 참조 그것에 대해 Kohsuke 가와구치의 블로그 게시물을 전체 세부 사항은 있지만, 짧은에, 당신은 사용할 수 있습니다new File(url.toURI()) URL의 형식이 올바른 한 .

마지막 으로을 사용 하지 않는 것이 좋습니다URLDecoder . 의 URL의 일부 문자 :/특히는 유효한 URL 인코딩 된 문자 수 없습니다. 로부터 URLDecoder의 자바 독 :

인코딩 된 문자열의 모든 문자는 “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 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을 수동으로 분리해야합니다. jar URL 형식에 대해서는 JarClassLoader 학습서 를 참조하십시오 .


답변

최근에 사용을 제안한 사람이 없다는 것이 놀랍습니다 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();
}