Làm thế nào để có được đường dẫn của tệp JAR đang chạy?


580

Mã của tôi chạy bên trong tệp JAR, giả sử foo.jar và tôi cần biết, trong mã, trong thư mục nào đang chạy foo.jar .

Vì vậy, nếu foo.jar ở trong C:\FOO\, tôi muốn có được đường dẫn đó bất kể thư mục làm việc hiện tại của tôi là gì.


Xem câu trả lời của Fab để biết giải pháp hoạt động khi đường dẫn bao gồm khoảng trắng. Ngoài ra, lưu ý rằng một số câu trả lời bên dưới giải quyết câu hỏi trong tiêu đề (đường dẫn jar), một số câu trả lời cho chính câu hỏi (đường dẫn của thư mục chứa jar) và một số cung cấp đường dẫn đến các lớp bên trong tệp jar.
Andy Thomas

32
Cẩn thận khi sử dụng trong ANT! ============== Tôi gọi String path = someClass. Class.getProtectionDomain (). GetCodeSource (). GetLocation (). GetPath (); và nhận: /C:/apache-ant-1.7.1/lib/ant.jar Không hữu ích lắm!
Dino Fancellu

Hấp dẫn. Mã ban đầu mà tôi đã sử dụng này không bao giờ chạy trong ant, vì vậy nó không phải là vấn đề đối với tôi.
Thiago Chaves

2
@Dino Fancellu, tôi đã trải nghiệm chính xác những gì bạn mô tả. Hoạt động trong thời gian dev, thất bại khi xây dựng để jar.
Buddy

Câu trả lời:


539
return new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation()
    .toURI()).getPath();

Thay thế "MyClass" bằng tên của lớp bạn.

Rõ ràng, điều này sẽ làm những điều kỳ lạ nếu lớp của bạn được tải từ một vị trí không phải là tệp.


43
Các toURI()bước quan trọng đến các vấn đề tránh với ký tự đặc biệt, bao gồm cả dấu và dấu cộng. Một lớp lót chính xác là: return new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation().toURI()); Sử dụng URLDecoderkhông hoạt động đối với nhiều ký tự đặc biệt. Xem câu trả lời của tôi dưới đây để biết thêm chi tiết.
ctrueden

4
Lưu ý: điều này trả về đường dẫn bao gồm tên của tệp jar
Buddy

8
Không phải điều này trỏ đến tệp jar, thay vì thư mục đang chạy? Bạn sẽ phải thực hiện kết quả getParentFile () cho công việc này.
FOO

1
Ngoài ra, getProtectionDomainlà null nếu bạn nhận được lớp của mình từ một chiến lược:val strace = Thread.currentThread().getStackTrace; val path = strace(1).getClass.getProtectionDomain
bbarker

1
Sử dụng phương thức này với tối đa Java 8; đặt phương thức này trong một lớp nằm trong Jar bên ngoài, được tải thông qua đường dẫn lớp, sau đó đường dẫn của jar bên ngoài sẽ được đưa ra thay vì Jar đang chạy thực tế.
Mr00Anderson

189

Giải pháp tốt nhất cho tôi:

String path = Test.class.getProtectionDomain().getCodeSource().getLocation().getPath();
String decodedPath = URLDecoder.decode(path, "UTF-8");

Điều này sẽ giải quyết vấn đề với không gian và các ký tự đặc biệt.


8
Thêm một lưu ý: Trong khi gọi hàm này từ Jar, tên của jar được gắn vào cuối cho tôi, do đó phải thực thi: path.sub chuỗi (0, path.lastIndexOf ("/") + 1);
will824

11
/ không nhất thiết phải là dấu phân cách đường dẫn. Bạn nên làm (Tệp mới (đường dẫn)). GetParentFile (). GetPath () thay thế.
pjz

10
Không có vấn đề với tên tệp JAR được thêm vào đây. Chuyển đổi UTF dường như là giải pháp hoàn hảo kết hợp với @Iviggiani ( URLDecoder.decode(ClassLoader.getSystemClassLoader().getResource(".").getPath(), "UTF-8");) trên Linux. Tuy nhiên, tôi đã không thử trên Windows.
ubuntudroid

2
Cảm ơn bạn, điều này cho phép tôi tải các tệp bên ngoài vào JAR của mình bằng FileInputStream trong cả Linux và Windows. Chỉ cần thêm đường dẫn được giải mã ở phía trước tên tệp ...
giorgio79

11
Chú ý: không nên sử dụng URLDecoderđể giải mã các ký tự đặc biệt. Cụ thể, các ký tự như +sẽ bị giải mã sai thành khoảng trắng. Xem câu trả lời của tôi để biết chi tiết.
ctrueden

153

Để có được Filemột cái cho trước Class, có hai bước:

  1. Chuyển đổi Classthành mộtURL
  2. Chuyển đổi URLthành mộtFile

Điều quan trọng là phải hiểu cả hai bước, và không giới thiệu chúng.

Một khi bạn có File, bạn có thể gọigetParentFile để lấy thư mục chứa, nếu đó là thứ bạn cần.

Bước 1: ClassđếnURL

Như đã thảo luận trong các câu trả lời khác, có hai cách chính để tìm một URLliên quan đến a Class.

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

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

Cả hai đều có ưu và nhược điểm.

Cách getProtectionDomaintiếp cận mang lại vị trí cơ sở của lớp (ví dụ: tệp JAR chứa). Tuy nhiên, có thể chính sách bảo mật của thời gian chạy Java sẽ xuất hiện SecurityExceptionkhi gọi getProtectionDomain(), vì vậy nếu ứng dụng của bạn cần chạy trong nhiều môi trường khác nhau, tốt nhất nên thử nghiệm tất cả chúng.

Cách getResourcetiếp cận mang lại đường dẫn tài nguyên URL đầy đủ của lớp, từ đó bạn sẽ cần thực hiện thao tác chuỗi bổ sung. Nó có thể là một file:đường dẫn, nhưng nó cũng có thể jar:file:hoặc thậm chí là một thứ gì đó khó chịu hơn bundleresource://346.fwk2106232034:4/foo/Bar.classkhi thực thi trong khung OSGi. Ngược lại, getProtectionDomaincách tiếp cận chính xác mang lại mộtfile: URL ngay cả trong OSGi.

Lưu ý rằng cả hai getResource("")getResource(".")đều thất bại trong các thử nghiệm của tôi, khi lớp nằm trong tệp JAR; cả hai lời mời đều trả về null. Vì vậy, tôi đề nghị cách gọi số 2 được hiển thị ở trên, vì nó có vẻ an toàn hơn.

Bước 2: URLđếnFile

Dù bằng cách nào, một khi bạn có a URL, bước tiếp theo là chuyển đổi thành a File. Đây là thách thức riêng của nó; xem bài đăng trên blog của Kohsuke Kawaguchi về nó để biết chi tiết đầy đủ, nhưng tóm lại, bạn có thể sử dụngnew File(url.toURI()) miễn là URL hoàn toàn được định dạng.

Cuối cùng, tôi rất không khuyến khích sử dụng URLDecoder. Một số ký tự của URL :/đặc biệt là các ký tự được mã hóa URL hợp lệ. Từ Javadoc URLDecoder :

Giả định rằng tất cả các ký tự trong chuỗi được mã hóa là một trong những điều sau đây: "a" đến "z", "A" đến "Z", "0" đến "9" và "-", "_", " . "và" * ". Ký tự "%" được cho phép nhưng được hiểu là bắt đầu một chuỗi thoát đặc biệt.

...

Có hai cách có thể trong đó bộ giải mã này có thể xử lý các chuỗi bất hợp pháp. Nó có thể để các nhân vật bất hợp pháp một mình hoặc nó có thể ném IllegalArgumentException. Cách tiếp cận nào bộ giải mã được để lại cho việc thực hiện.

Trong thực tế, URLDecodernói chung không ném IllegalArgumentExceptionnhư bị đe dọa ở trên. Và nếu đường dẫn tệp của bạn có không gian được mã hóa dưới dạng %20, phương pháp này có thể hoạt động. Tuy nhiên, nếu đường dẫn tệp của bạn có các ký tự không phải là chữ và số khác, chẳng hạn như +bạn sẽ gặp vấn đề với việc URLDecoderxáo trộn đường dẫn tệp của mình.

Mã làm việc

Để đạt được các bước này, bạn có thể có các phương pháp như sau:

/**
 * 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);
}

Bạn có thể tìm thấy các phương thức này trong thư viện chung SciJava :


5
+1; câu trả lời tốt nhất cho đến nay: nó sẽ trả về đường dẫn sử dụng ký hiệu chính xác cho HĐH. (ví dụ \ cho các cửa sổ).
Bathsheba

Về bảo mật, tôi tin rằng tôi thấy rằng Java WebStart không cho phép điều này.
Thorbjørn Ravn Andersen

55

Bạn cũng có thể dùng:

CodeSource codeSource = YourMainClass.class.getProtectionDomain().getCodeSource();
File jarFile = new File(codeSource.getLocation().toURI().getPath());
String jarDir = jarFile.getParentFile().getPath();

3
Điều này hoạt động tốt hơn đối với tôi, vì nó đưa ra con đường của Jar, không phải của lớp!
T30

Làm việc cho tôi quá. Kết hợp với câu trả lời của Fab và nó sẽ tốt hơn!
Danielson Alves Júnior

25

Sử dụng ClassLoader.getResource () để tìm URL cho lớp hiện tại của bạn.

Ví dụ:

package foo;

public class Test
{
    public static void main(String[] args)
    {
        ClassLoader loader = Test.class.getClassLoader();
        System.out.println(loader.getResource("foo/Test.class"));
    }
}

(Ví dụ này được lấy từ một câu hỏi tương tự .)

Để tìm thư mục, sau đó bạn cần tách URL theo cách thủ công. Xem hướng dẫn JarClassLoader để biết định dạng của URL jar.


Tệp JAR của tôi bị xáo trộn, vì vậy câu trả lời này không giải quyết được vấn đề của tôi. Nhưng tôi đã không xác định rằng trong câu hỏi, vì vậy đây vẫn là một câu trả lời hợp lệ.
Thiago Chaves

12
Nếu nó bị xáo trộn, hãy sử dụng Test.group.getName () và thực hiện thao tác trộn thích hợp.
Jon Skeet

1
@JonSkeet có rất nhiều vấn đề với câu trả lời của bạn: 1. Sẽ không có NPEvì bạn đã không trả lời cho câu hỏi đã được hỏi (đường dẫn đến thư mục JAR đã được hỏi và bạn đã trả lời câu hỏi hoàn toàn khác: đường đến lớp). 2. Như được chỉ ra bởi những người khác, và tôi đã có cùng một vấn đề, nó không hoạt động cho các applet. 3. Đường dẫn trả về hoàn toàn không phải là biểu diễn đường dẫn chính tắc : jar:file:/listener/build/libs/listener-1.0.0-all.jar!/shared/Test.class.
WhiteAngel

1
@WhiteAngel: 1) Dòng cuối cùng của bài viết của tôi chỉ ra rằng bạn cần xem URL và chọn cách đó để lấy tệp jar. Tôi đồng ý rằng đó không phải là câu trả lời đầy đủ nhất, nhưng tôi không nghĩ nó thực sự tệ đến mức đáng để tranh cãi (đặc biệt là 10 năm sau ...) 2) Các ứng dụng không được đề cập trong bất kỳ bình luận nào ở đây - thật kỳ lạ, tôi không Tôi không có thời gian để xem tất cả các nhận xét về tất cả các câu trả lời cho các câu hỏi mà tôi tình cờ đăng câu trả lời. 3) Một lần nữa, tôi liên kết đến định dạng của URL jar.
Jon Skeet

2
@WhiteAngel: Đó có phải là câu trả lời hay nhất tôi từng viết không? Không. Nó có tệ như bạn đang làm không? Không, tôi không nghĩ vậy. (Đặc biệt là về các khiếu nại mà bạn đã đưa ra xung quanh việc ném NPE mà không có.) Tôi sẽ đề nghị bạn thêm câu trả lời của riêng bạn thay vì làm ầm ĩ về vấn đề này. Đó sẽ là một cách tiếp cận tích cực hơn.
Jon Skeet

19

Tôi ngạc nhiên khi thấy rằng gần đây không có đề xuất sử dụng Path. Ở đây sau một trích dẫn: " Các Pathlớp bao gồm các phương pháp khác nhau có thể được sử dụng để có được thông tin về đường dẫn, các yếu tố tiếp cận của con đường, chuyển đổi các đường dẫn đến các hình thức khác, hoặc các phần chiết xuất của một con đường "

Vì vậy, một lựa chọn tốt là nhận được sự Pathphản đối như:

Path path = Paths.get(Test.class.getProtectionDomain().getCodeSource().getLocation().toURI());

3
Lưu ý, Đường dẫn có sẵn bắt đầu trong Java 7.
Chris Forrence

15

Giải pháp duy nhất phù hợp với tôi trên Linux, Mac và 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();
}

Điều này sẽ không hoạt động. Nếu trên Linux, phương thức toUri () sẽ đưa ra một ngoại lệ và bạn sẽ không tiếp cận được phần khác, đối với linux.
Wilhelm Sorban

9

Tôi đã có cùng một vấn đề và tôi đã giải quyết nó theo cách đó:

File currentJavaJarFile = new File(Main.class.getProtectionDomain().getCodeSource().getLocation().getPath());   
String currentJavaJarFilePath = currentJavaJarFile.getAbsolutePath();
String currentRootDirectoryPath = currentJavaJarFilePath.replace(currentJavaJarFile.getName(), "");

Tôi hy vọng tôi đã giúp bạn.


Đừng làm vậy. URL.getPath () không trả về tên tệp và nó sẽ thất bại trong nhiều trường hợp, chẳng hạn như đường dẫn tệp có khoảng trắng trong đó.
VGR

9

Đây là bản nâng cấp cho các bình luận khác, dường như tôi chưa hoàn thiện về các chi tiết cụ thể của

sử dụng "thư mục" tương đối bên ngoài tệp .jar (trong cùng vị trí của 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"));

4
Chú ý: không nên sử dụng URLDecoderđể giải mã các ký tự đặc biệt. Cụ thể, các ký tự như +sẽ bị giải mã sai thành khoảng trắng. Xem câu trả lời của tôi để biết chi tiết.
ctrueden

Không nên sử dụng các ký tự đặc biệt trong tên tệp.
Zon

URLDecoder, mặc dù tên của nó, là để giải mã URL và hình thức tên và giá trị tham số, không phải URL.
Hầu tước Lorne

6

Để có được đường dẫn chạy tệp jar, tôi đã nghiên cứu các giải pháp trên và thử tất cả các phương thức tồn tại một số khác biệt. Nếu các mã này đang chạy trong IDE Eclipse, tất cả chúng sẽ có thể tìm thấy đường dẫn của tệp bao gồm lớp được chỉ định và mở hoặc tạo một tệp được chỉ định với đường dẫn tìm thấy.

Nhưng thật khó khăn, khi chạy tệp jar có thể chạy trực tiếp hoặc thông qua dòng lệnh, nó sẽ thất bại vì đường dẫn của tệp jar nhận được từ các phương thức trên sẽ đưa ra một đường dẫn nội bộ trong tệp jar, đó là luôn luôn cung cấp một đường dẫn như

rsrc: tên dự án (có lẽ tôi nên nói rằng đó là tên gói của tệp lớp chính - lớp được chỉ định)

Tôi không thể chuyển đổi đường dẫn rsrc: ... thành đường dẫn bên ngoài, đó là khi chạy tệp jar bên ngoài IDE Eclipse, nó không thể lấy đường dẫn của tệp jar.

Cách khả thi duy nhất để có được đường dẫn chạy tệp jar bên ngoài IDE Eclipse là

System.getProperty("java.class.path")

dòng mã này có thể trả về đường dẫn sống (bao gồm tên tệp) của tệp jar đang chạy (lưu ý rằng đường dẫn trả lại không phải là thư mục làm việc), vì tài liệu java và một số người nói rằng nó sẽ trả về đường dẫn của tất cả các tệp lớp trong cùng một thư mục, nhưng như các thử nghiệm của tôi nếu trong cùng một thư mục bao gồm nhiều tệp jar, nó chỉ trả về đường dẫn chạy jar (về vấn đề nhiều đường dẫn thực sự đã xảy ra trong Eclipse).


java.class.pathcó thể được đa trị hóa. Một trong những giá trị đó chắc chắn sẽ cung cấp thư mục hoặc tệp JAR nơi lớp hiện tại được đặt, nhưng cái nào?
Hầu tước Lorne

Tôi xác nhận, tôi đã thử các giải pháp khác, nhưng không bao giờ có được tên tệp jar. Điều này hoạt động rất đơn giản! cảm ơn - +1
guillaume girod-vitouchkina

5

Các câu trả lời khác dường như trỏ đến nguồn mã là vị trí tệp Jar không phải là thư mục.

Sử dụng

return new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getParentFile();

Nó có thể là một thư mục, nếu bạn đang tải các lớp của mình từ một hệ thống tệp thay vì tệp JAR, ví dụ như khi gỡ lỗi.
Hầu tước Lorne

4

câu trả lời được chọn ở trên không hoạt động nếu bạn chạy jar của mình bằng cách nhấp vào nó từ môi trường máy tính để bàn Gnome (không phải từ bất kỳ tập lệnh hoặc thiết bị đầu cuối nào).

Thay vào đó, tôi thích rằng giải pháp sau đây đang hoạt động ở mọi nơi:

    try {
        return URLDecoder.decode(ClassLoader.getSystemClassLoader().getResource(".").getPath(), "UTF-8");
    } catch (UnsupportedEncodingException e) {
        return "";
    }

2
Bạn đã thử điều đó trong một applet, hay một ứng dụng. được khởi chạy bằng Java Web Start? Sự hiểu biết của tôi là nó sẽ thất bại trong cả hai tình huống (ngay cả khi ứng dụng được tin cậy).
Andrew Thompson

Giải pháp này chỉ có thể trả về vị trí của "." trong tệp JAR, không phải vị trí của tệp JAR.
Hầu tước Lorne

Chú ý: không nên sử dụng URLDecoderđể giải mã các ký tự đặc biệt. Cụ thể, các ký tự như +sẽ bị giải mã sai thành khoảng trắng. Xem câu trả lời của tôi để biết chi tiết.
ctrueden

Trong khởi động mùa xuân, nó sẽ ném NullPointerException
Ravi Parekh

Bạn sẽ có NPEnếu không có tài nguyên trong JAR.
WhiteAngel

3

Trên thực tế đây là một phiên bản tốt hơn - phiên bản cũ đã thất bại nếu tên thư mục có khoảng trắng trong đó.

  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);
  }

Đối với thất bại với các applet, bạn thường sẽ không có quyền truy cập vào các tệp cục bộ. Tôi không biết nhiều về JWS nhưng để xử lý các tệp cục bộ thì có thể không tải được ứng dụng.?


Có một số cách tích hợp để giải mã đường dẫn. Không cần phải viết mã của riêng bạn.
Hầu tước Lorne

3

Tôi đã cố gắng để có được đường chạy jar bằng cách sử dụng

String folder = MyClassName.class.getProtectionDomain().getCodeSource().getLocation().getPath();

c: \ app> java -jar application.jar

Chạy ứng dụng jar có tên "application.jar", trên Windows trong thư mục " c: \ app ", giá trị của biến String "thư mục" là " \ c: \ app \ application.jar " và tôi gặp vấn đề khi kiểm tra sự đúng đắn của con đường

File test = new File(folder);
if(file.isDirectory() && file.canRead()) { //always false }

Vì vậy, tôi đã cố gắng định nghĩa "kiểm tra" là:

String fold= new File(folder).getParentFile().getPath()
File test = new File(fold);

để có được đường dẫn ở định dạng đúng như " c: \ app " thay vì " \ c: \ app \ application.jar " và tôi nhận thấy rằng nó hoạt động.


3

Giải pháp đơn giản nhất là vượt qua đường dẫn làm đối số khi chạy jar.

Bạn có thể tự động hóa điều này với một tập lệnh shell (.bat trong Windows, .sh ở bất kỳ nơi nào khác):

java -jar my-jar.jar .

Tôi đã sử dụng .để vượt qua thư mục làm việc hiện tại.

CẬP NHẬT

Bạn có thể muốn dán tệp jar trong thư mục con để người dùng không vô tình nhấp vào tệp. Mã của bạn cũng nên kiểm tra để đảm bảo rằng các đối số dòng lệnh đã được cung cấp và cung cấp một thông báo lỗi tốt nếu các đối số bị thiếu.


3

Tôi đã phải loay hoay rất nhiều trước khi cuối cùng tôi tìm thấy một giải pháp hiệu quả (và ngắn).
Có thể là jarLocationđi kèm với một tiền tố như file:\hoặc jar:file\, có thể được loại bỏ bằng cách sử dụng String#substring().

URL jarLocationUrl = MyClass.class.getProtectionDomain().getCodeSource().getLocation();
String jarLocation = new File(jarLocationUrl.toString()).getParent();

2
String path = getClass().getResource("").getPath();

Đường dẫn luôn đề cập đến tài nguyên trong tệp jar.


1
Chuỗi đường dẫn đó vẫn cần được đơn giản hóa theo nhu cầu của bạn. String path = new File(getClass().getResource("").getPath()).getParentFile().getParent(); File jarDir = new File(path.substring(5));
ZZZ

4
Cả hai getResource("")và đều getResource(".")thất bại trong các bài kiểm tra của tôi, khi lớp nằm trong tệp JAR; cả hai lời mời đều trả về null.
ctrueden

2
Cái này ném NullPointerException.
Hầu tước Lorne

2
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;}

Hoạt động tốt trên Windows


1

Một điều gây nản lòng là khi bạn đang phát triển trong Eclipse MyClass.class.getProtectionDomain().getCodeSource().getLocation()trả về /binthư mục rất tuyệt, nhưng khi bạn biên dịch nó vào một tệp jar, đường dẫn bao gồm /myjarname.jarphần cung cấp cho bạn tên tệp bất hợp pháp.

Để có mã hoạt động cả trong ide và một khi nó được biên dịch vào một jar, tôi sử dụng đoạn mã sau:

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");
}

1

Không thực sự chắc chắn về những người khác nhưng trong trường hợp của tôi, nó không hoạt động với "Bình có thể chạy được" và tôi đã làm cho nó hoạt động bằng cách sửa mã cùng nhau từ câu trả lời phchen2 và một liên kết khác từ liên kết này: Làm thế nào để có được đường dẫn của tệp JAR đang chạy? Mật mã:

               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");

1

Đã thử một vài giải pháp trên đó nhưng không có kết quả chính xác nào cho trường hợp (có lẽ đặc biệt) rằng bình có thể chạy được đã được xuất với "Đóng gói thư viện bên ngoài" trong Eclipse. Vì một số lý do, tất cả các giải pháp dựa trên ProtectionDomain đều không có kết quả trong trường hợp đó.

Từ việc kết hợp một số giải pháp ở trên, tôi đã quản lý để đạt được mã làm việc sau:

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);

1

Thử cái này:

String path = new File("").getAbsolutePath();

0

Phương thức này, được gọi từ mã trong kho lưu trữ, trả về thư mục chứa tệp .jar. Nó nên hoạt động trong cả Windows hoặc 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);
  } 

Xuất phát từ mã tại: Xác định nếu chạy từ JAR


3
"Nó nên hoạt động trong cả Windows hoặc Unix." nhưng sẽ thất bại trong bất kỳ applet và mọi ứng dụng. ra mắt bằng JWS.
Andrew Thompson

0

Đề cập rằng nó chỉ được kiểm tra Windowsnhưng tôi nghĩ nó hoạt động hoàn hảo trên các Hệ điều hành khác [ Linux,MacOs,Solaris] :).


Tôi đã có 2 .jar tập tin trong cùng một thư mục. Tôi muốn từ một .jartệp để bắt đầu .jartệp khác trong cùng thư mục.

Vấn đề là khi bạn khởi động nó từ cmdthư mục hiện tại system32.


Cảnh báo!

  • Dưới đây dường như hoạt động khá tốt trong tất cả các thử nghiệm tôi đã thực hiện ngay cả với tên thư mục ;][[;'57f2g34g87-8+9-09!2#@!$%^^&()hoặc()%&$%^@# nó hoạt động tốt.
  • Tôi đang sử dụng ProcessBuildervới dưới đây như sau:

🍂 ..

//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's 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;
    }

0

Mã này làm việc cho tôi:

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;
  }

0

Mã này làm việc cho tôi để xác định xem chương trình đang được thực thi trong tệp JAR hay IDE:

private static boolean isRunningOverJar() {
    try {
        String pathJar = Application.class.getResource(Application.class.getSimpleName() + ".class").getFile();

        if (pathJar.toLowerCase().contains(".jar")) {
            return true;
        } else {
            return false;
        }
    } catch (Exception e) {
        return false;
    }
}

Nếu tôi cần lấy đường dẫn Windows đầy đủ của tệp JAR, tôi đang sử dụng phương pháp này:

    private static String getPathJar() {
        try {
            final URI jarUriPath =
                    Application.class.getResource(Application.class.getSimpleName() + ".class").toURI();
            String jarStringPath = jarUriPath.toString().replace("jar:", "");
            String jarCleanPath  = Paths.get(new URI(jarStringPath)).toString();

            if (jarCleanPath.toLowerCase().contains(".jar")) {
                return jarCleanPath.substring(0, jarCleanPath.lastIndexOf(".jar") + 4);
            } else {
                return null;
            }
        } catch (Exception e) {
            log.error("Error getting JAR path.", e);
            return null;
        }
    }

Mã hoàn chỉnh của tôi hoạt động với ứng dụng Spring Boot bằng cách sử dụng CommandLineRunnertriển khai, để đảm bảo rằng ứng dụng luôn được thực thi trong chế độ xem bảng điều khiển (Nhấp đúp do nhầm lẫn trong tên tệp JAR), tôi đang sử dụng mã tiếp theo:

@SpringBootApplication
public class Application implements CommandLineRunner {
    public static void main(String[] args) throws IOException {
        Console console = System.console();

        if (console == null && !GraphicsEnvironment.isHeadless() && isRunningOverJar()) {
            Runtime.getRuntime().exec(new String[]{"cmd", "/c", "start", "cmd", "/k",
                    "java -jar \"" + getPathJar() + "\""});
        } else {
            SpringApplication.run(Application.class, args);
        }
    }

    @Override
    public void run(String... args) {
        /*
        Additional code here...
        */
    }

    private static boolean isRunningOverJar() {
        try {
            String pathJar = Application.class.getResource(Application.class.getSimpleName() + ".class").getFile();

            if (pathJar.toLowerCase().contains(".jar")) {
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            return false;
        }
    }

    private static String getPathJar() {
        try {
            final URI jarUriPath =
                    Application.class.getResource(Application.class.getSimpleName() + ".class").toURI();
            String jarStringPath = jarUriPath.toString().replace("jar:", "");
            String jarCleanPath  = Paths.get(new URI(jarStringPath)).toString();

            if (jarCleanPath.toLowerCase().contains(".jar")) {
                return jarCleanPath.substring(0, jarCleanPath.lastIndexOf(".jar") + 4);
            } else {
                return null;
            }
        } catch (Exception e) {
            return null;
        }
    }
}

-1

Tôi viết bằng Java 7 và thử nghiệm trong Windows 7 với thời gian chạy của Oracle và Ubuntu với thời gian chạy nguồn mở. Điều này hoạt động hoàn hảo cho các hệ thống:

Đường dẫn cho thư mục mẹ của bất kỳ tệp jar đang chạy nào (giả sử lớp gọi mã này là con trực tiếp của chính tệp lưu trữ 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

Vì vậy, đường dẫn của foo.jar sẽ là:

fooPath = fooDirPath + File.separator + "foo.jar";

Một lần nữa, điều này không được thử nghiệm trên bất kỳ máy Mac hoặc Windows cũ hơn


-1

Cách getProtectionDomaintiếp cận đôi khi có thể không hoạt động, ví dụ như khi bạn phải tìm jar cho một số lớp java cốt lõi (ví dụ: trong StringBuilderlớp trường hợp của tôi trong IBM JDK), tuy nhiên sau đây hoạt động trơn tru:

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, "");
}

URL.getPath () không làm những gì bạn nghĩ. Bất kỳ ký tự đặc biệt sẽ được mã hóa phần trăm.
VGR

-1

Tôi có một cách khác để có được vị trí Chuỗi của một lớp.

URL path = Thread.currentThread().getContextClassLoader().getResource("");
Path p = Paths.get(path.toURI());
String location = p.toString();

Chuỗi đầu ra sẽ có dạng

C:\Users\Administrator\new Workspace\...

Các không gian và các ký tự khác được xử lý, và ở dạng không có file:/. Vì vậy sẽ dễ sử dụng hơn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.