วิธีรับพา ธ ของไฟล์ JAR ที่ใช้งานอยู่


580

รหัสของฉันทำงานในไฟล์ JAR พูดว่าfoo.jarและฉันจำเป็นต้องรู้ในรหัสว่าโฟลเดอร์ใดที่foo.jarกำลังเรียกใช้อยู่

ดังนั้นถ้าfoo.jarอยู่ในC:\FOO\ฉันต้องการได้รับเส้นทางนั้นไม่ว่าไดเรกทอรีทำงานปัจจุบันของฉันคืออะไร


ดูคำตอบของ Fab สำหรับโซลูชันที่ทำงานเมื่อพา ธ มีช่องว่าง นอกจากนี้โปรดทราบว่าบางคำตอบด้านล่างตอบคำถามในชื่อเรื่อง (พา ธ jar) บางคนตอบคำถามด้วยตัวเอง (พา ธ ของโฟลเดอร์ที่มี jar) และบางส่วนให้เส้นทางไปยังคลาสภายในไฟล์ jar
Andy Thomas

32
ระวังเมื่อใช้ใน ANT! ============== ฉันเรียกเส้นทางสตริง = SomeClass.class.getProtectionDomain (). getCodeSource (). getLocation (). getPath (); และรับ: /C:/apache-ant-1.7.1/lib/ant.jar ไม่ค่อยมีประโยชน์!
Dino Fancellu

น่าสนใจ รหัสเดิมที่ฉันใช้นี้ไม่เคยทำงานในมดดังนั้นมันจึงไม่เป็นปัญหาสำหรับฉัน
Thiago Chaves

2
@Dino Fancellu ฉันมีประสบการณ์ตรงกับสิ่งที่คุณอธิบาย ทำงานระหว่าง dev ไม่สำเร็จเมื่อสร้างเป็น jar
บัดดี้

คำตอบ:


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

แทนที่ "MyClass" ด้วยชื่อคลาสของคุณ

เห็นได้ชัดว่าสิ่งนี้จะทำสิ่งแปลกถ้าชั้นของคุณถูกโหลดจากที่ไม่ใช่ไฟล์


43
toURI()ขั้นตอนที่มีความสำคัญต่อปัญหาหลีกเลี่ยงการมีตัวอักษรพิเศษรวมถึงช่องว่างและ pluses หนึ่งซับที่ถูกต้องคือ: return new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation().toURI()); การใช้URLDecoderไม่สามารถใช้ได้กับอักขระพิเศษจำนวนมาก ดูคำตอบของฉันด้านล่างสำหรับรายละเอียดเพิ่มเติม
ctrueden

4
หมายเหตุ: นี่จะคืนค่าพา ธ รวมถึงชื่อไฟล์ jar
Buddy

8
นี่ชี้ไปที่ไฟล์ jar ไม่ใช่ไดเรกทอรีที่กำลังทำงานใช่ไหม คุณจะต้องทำผลการ getParentFile () สำหรับงานนี้
FOO

1
นอกจากนี้ยังgetProtectionDomainเป็นโมฆะถ้าคุณได้รับชั้นเรียนของคุณจาก stracktrace:val strace = Thread.currentThread().getStackTrace; val path = strace(1).getClass.getProtectionDomain
bbarker

1
ใช้วิธีนี้ด้วยมากถึง Java 8 วางเมธอดนี้ในคลาสที่อยู่ใน Jar ภายนอกโหลดผ่านคลาสพา ธ จากนั้นพา ธ ของ jar ภายนอกจะถูกกำหนดแทนการรัน Jar จริง
Mr00Anderson

189

ทางออกที่ดีที่สุดสำหรับฉัน:

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

สิ่งนี้ควรแก้ปัญหาช่องว่างและอักขระพิเศษ


8
หมายเหตุเพิ่มเติมอีกหนึ่ง: ขณะเรียกใช้ฟังก์ชันนี้จาก Jar ชื่อของขวดจะต่อท้ายที่ฉันดังนั้นจึงต้องดำเนินการ: path.substring (0, path.lastIndexOf ("/") + 1);
will824

11
/ ไม่จำเป็นต้องมีตัวคั่นพา ธ คุณควรทำ (ไฟล์ใหม่ (พา ธ )) getParentFile (). getPath () แทน
pjz

10
ไม่มีปัญหากับชื่อไฟล์ JAR ที่ถูกผนวกที่นี่ การแปลง UTF ดูเหมือนจะเป็นโซลูชันที่สมบูรณ์แบบเมื่อใช้ร่วมกับ @Iviggiani one ( URLDecoder.decode(ClassLoader.getSystemClassLoader().getResource(".").getPath(), "UTF-8");) บน Linux อย่างไรก็ตามฉันไม่ได้ลองบน Windows
ubuntudroid

2
ขอบคุณนี้ทำให้ฉันสามารถโหลดไฟล์ภายนอก JAR ของฉันด้วย FileInputStream ทั้งใน Linux และ Windows เพียงแค่มีการเพิ่ม decodedpath ในด้านหน้าของชื่อไฟล์ ...
giorgio79

11
ระวัง: ไม่แนะนำให้ใช้URLDecoderในการถอดรหัสอักขระพิเศษ โดยเฉพาะอย่างยิ่งตัวละครที่ชอบ+จะถูกถอดรหัสไปยังช่องว่างอย่างผิดพลาด ดูคำตอบของฉันสำหรับรายละเอียด
ctrueden

153

เพื่อให้ได้รับFileสำหรับClassมีสองขั้นตอน:

  1. แปลงClassเป็นURL
  2. แปลงURLเป็นFile

เป็นสิ่งสำคัญที่จะต้องเข้าใจทั้งสองขั้นตอนและไม่ทำให้พวกเขาสับสน

เมื่อคุณมีFileคุณสามารถโทรgetParentFileโฟลเดอร์ที่มีหากนั่นคือสิ่งที่คุณต้องการ

ขั้นตอนที่ 1: ClassถึงURL

ตามที่กล่าวไว้ในคำตอบอื่น ๆ มีสองวิธีที่สำคัญในการหาที่เกี่ยวข้องกับURLClass

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

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

ทั้งสองมีข้อดีและข้อเสีย

getProtectionDomainวิธีถัวเฉลี่ยที่ตั้งฐานของการเรียน (เช่นไฟล์ JAR มี) อย่างไรก็ตามอาจเป็นไปได้ว่านโยบายความปลอดภัยของ Java runtime จะแสดงขึ้นSecurityExceptionเมื่อมีการโทรgetProtectionDomain()ดังนั้นหากแอปพลิเคชันของคุณต้องการทำงานในสภาพแวดล้อมที่หลากหลายจึงเป็นสิ่งที่ดีที่สุดในการทดสอบทั้งหมด

getResourceวิธีถัวเฉลี่ยเส้นทางทรัพยากร URL แบบเต็มของชั้นเรียนซึ่งคุณจะต้องดำเนินการจัดการสตริงเพิ่มเติม อาจเป็นfile:เส้นทาง แต่ก็อาจเป็นjar:file:หรือแม้แต่สิ่งที่น่าสนใจกว่าbundleresource://346.fwk2106232034:4/foo/Bar.classเมื่อดำเนินการภายในกรอบงาน OSGi ในทางกลับกันgetProtectionDomainวิธีการที่ให้ผลตอบแทนที่ถูกต้องfile: URL ได้แม้ใน OSGi

โปรดทราบว่าทั้งสองgetResource("")และgetResource(".")ล้มเหลวในการทดสอบของฉันเมื่อชั้นอยู่ในไฟล์ JAR; การร้องขอทั้งสองคืนค่า null ดังนั้นฉันขอแนะนำการเรียกใช้ # 2 ที่แสดงด้านบนแทนเนื่องจากดูเหมือนปลอดภัยกว่า

ขั้นตอนที่ 2: URLถึงFile

ทั้งสองวิธีเมื่อคุณมีขั้นตอนต่อไปคือการแปลงไปURL Fileนี่คือความท้าทายของตัวเอง ดูโพสต์บล็อกของ Kohsuke Kawaguchi เกี่ยวกับรายละเอียดแบบเต็ม แต่ในระยะสั้นคุณสามารถใช้new File(url.toURI())ตราบใดที่ URL มีรูปแบบที่สมบูรณ์

สุดท้ายนี้ผมจะขอกีดกันการ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 :


5
+1; คำตอบที่ดีที่สุดถึงวันที่: มันจะกลับเส้นทางโดยใช้สัญกรณ์ที่ถูกต้องสำหรับระบบปฏิบัติการ (เช่น \ สำหรับ windows)
Bathsheba

เกี่ยวกับความปลอดภัยฉันเชื่อว่าฉันพบว่า Java WebStart ไม่อนุญาต
Thorbjørn Ravn Andersen

55

คุณยังสามารถใช้:

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

3
มันใช้งานได้ดีกว่าสำหรับฉันเพราะมันให้เส้นทางของ Jar ไม่ใช่คลาส!
T30

ทำงานให้ฉันด้วย รวมเข้ากับคำตอบของ Fab และทำให้ดีขึ้น!
Danielson Alves Júnior

25

ใช้ 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


ไฟล์ JAR ของฉันยุ่งเหยิงดังนั้นคำตอบนี้ไม่ได้แก้ปัญหาของฉัน แต่ฉันไม่ได้ระบุไว้ในคำถามดังนั้นนี่จึงเป็นคำตอบที่ถูกต้อง
Thiago Chaves

12
ถ้ามันงงให้ใช้ Test.class.getName () และทำการ munging ที่เหมาะสม
Jon Skeet

1
@ JonSkeet มีปัญหามากมายกับคำตอบของคุณ: 1. จะไม่มีNPEเพราะคุณไม่ได้ตอบคำถามที่ถูกถาม (เส้นทางไปยัง JAR dir ถูกถามและคุณตอบคำถามที่แตกต่างกันอย่างสิ้นเชิง: เส้นทางสู่ชั้นเรียน) 2. ตามที่คนอื่น ๆ ชี้และฉันได้รับปัญหาเดียวกันมันไม่ทำงานสำหรับแอปเพล็ต 3. เส้นทางที่ส่งคืนไม่ใช่การแสดงเส้นทางแบบบัญญัติทั้งหมด: jar:file:/listener/build/libs/listener-1.0.0-all.jar!/shared/Test.class.
WhiteAngel

1
@WhiteAngel: 1) บรรทัดสุดท้ายของโพสต์ของฉันระบุว่าคุณจะต้องดู URL และเลือกแยกต่างหากเพื่อรับไฟล์ jar ฉันเห็นด้วยว่ามันไม่ใช่คำตอบที่สมบูรณ์ที่สุด แต่ฉันไม่คิดว่ามันจะเลวร้ายนักที่จะเถียงกัน (โดยเฉพาะ 10 ปีต่อมา ... ) 2) Applets ไม่ได้ถูกกล่าวถึงในความคิดเห็นใด ๆที่นี่ - แปลกพอฉันไม่ได้ ไม่มีเวลาดูความคิดเห็นทั้งหมดเกี่ยวกับคำตอบทั้งหมดสำหรับคำถามที่ฉันเกิดขึ้นเพื่อโพสต์คำตอบ 3) อีกครั้งฉันเชื่อมโยงกับรูปแบบของโถ URL
Jon Skeet

2
@ WhiteAngel: มันเป็นคำตอบที่ดีที่สุดที่ฉันเคยเขียน? Nope มันเลวร้ายอย่างที่คุณคิดหรือไม่? ไม่ฉันไม่คิดอย่างนั้น (โดยเฉพาะอย่างยิ่งในแง่ของการอ้างสิทธิ์ที่คุณทำไว้โดยการขว้าง NPE ซึ่งไม่เป็นเช่นนั้น) ฉันขอแนะนำให้คุณเพิ่มคำตอบของคุณเองแทนที่จะทำเรื่องที่ยุ่งยาก นั่นจะเป็นแนวทางเชิงบวกมากขึ้น
Jon Skeet

19

Pathผมแปลกใจที่จะเห็นใครที่เพิ่งเสนอให้ใช้ นี่ต่อไปนี้อ้างอิง: " ระดับรวมถึงวิธีการต่าง ๆ ที่สามารถนำมาใช้เพื่อให้ได้ข้อมูลเกี่ยวกับเส้นทางการเข้าถึงองค์ประกอบของเส้นทาง, แปลงเส้นทางไปยังรูปแบบอื่น ๆ หรือบางส่วนสารสกัดจากเส้นทาง "Path

ดังนั้นทางเลือกที่ดีคือการได้รับPathobjest เป็น:

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

3
เส้นทางสามารถใช้งานได้เริ่มต้นใน Java 7
Chris Forrence

15

ทางออกเดียวที่เหมาะกับฉันบน 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();
}

สิ่งนี้จะไม่ทำงาน หากบน Linux เมธอด toUri () จะทำให้เกิดข้อยกเว้นและคุณจะไม่สามารถเข้าถึงส่วนอื่นสำหรับ linux
Wilhelm Sorban

9

ฉันมีปัญหาเดียวกันและฉันแก้ไขมันด้วยวิธีนี้:

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

ฉันหวังว่าฉันจะช่วยคุณ


อย่าทำอย่างนั้น URL.getPath () ไม่ส่งคืนชื่อไฟล์และจะล้มเหลวในหลาย ๆ สถานการณ์เช่นเส้นทางของไฟล์ที่มีช่องว่าง
VGR

9

นี่คือการอัปเกรดเป็นความคิดเห็นอื่น ๆ ซึ่งดูเหมือนว่าฉันจะไม่สมบูรณ์สำหรับข้อมูลเฉพาะของ

ใช้ "โฟลเดอร์" สัมพัทธ์นอกไฟล์. jar (ในตำแหน่งเดียวกันของ 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
ระวัง: ไม่แนะนำให้ใช้URLDecoderในการถอดรหัสอักขระพิเศษ โดยเฉพาะอย่างยิ่งตัวละครที่ชอบ+จะถูกถอดรหัสไปยังช่องว่างอย่างผิดพลาด ดูคำตอบของฉันสำหรับรายละเอียด
ctrueden

ไม่แนะนำให้ใช้อักขระพิเศษในชื่อไฟล์
Zon

URLDecoderแม้จะมีชื่อเป็นของมันสำหรับการถอดรหัส URL และชื่อพารามิเตอร์และค่าฟอร์มไม่ใช่ URL
มาร์ควิสแห่ง Lorne

6

เพื่อให้ได้เส้นทางของการรันไฟล์ jar ฉันได้ศึกษาวิธีแก้ไขปัญหาข้างต้นแล้วลองใช้วิธีการทั้งหมดที่มีความแตกต่างกันบ้าง หากรหัสเหล่านี้กำลังทำงานใน Eclipse IDE พวกเขาควรจะสามารถค้นหาพา ธ ของไฟล์รวมถึงคลาสที่ระบุและเปิดหรือสร้างไฟล์ที่ระบุพร้อมกับพา ธ ที่พบ

แต่มันเป็นเรื่องยากเมื่อรันไฟล์ jar ที่รันได้โดยตรงหรือผ่านบรรทัดคำสั่งมันจะล้มเหลวเนื่องจากพา ธ ของไฟล์ jar ที่ได้รับจากวิธีการด้านบนจะให้พา ธ ภายในในไฟล์ jar ซึ่งจะให้เส้นทางเสมอ เช่น

rsrc: project-name (บางทีฉันควรจะบอกว่ามันเป็นชื่อแพ็กเกจของไฟล์คลาสหลัก - คลาสที่ระบุ)

ฉันไม่สามารถแปลงเส้นทาง rsrc: ... ไปเป็นเส้นทางภายนอกนั่นคือเมื่อเรียกใช้ไฟล์ jar นอก Eclipse IDE จะไม่สามารถรับเส้นทางของไฟล์ jar ได้

วิธีเดียวที่เป็นไปได้สำหรับการรับพา ธ ของการรันไฟล์ jar นอก Eclipse IDE คือ

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

บรรทัดรหัสนี้อาจส่งคืนเส้นทางที่มีชีวิต (รวมถึงชื่อไฟล์) ของไฟล์ jar ที่กำลังทำงานอยู่ (โปรดทราบว่าเส้นทางส่งคืนไม่ใช่ไดเรกทอรีทำงาน) เนื่องจากเอกสาร java และบางคนบอกว่าจะคืนค่าพา ธ ของไฟล์คลาสทั้งหมด ในไดเรกทอรีเดียวกัน แต่จากการทดสอบของฉันหากในไดเรกทอรีเดียวกันมีไฟล์ jar จำนวนมากมันจะส่งคืนพา ธ ของการรัน jar เท่านั้น (เกี่ยวกับปัญหาหลายพา ธ ที่เกิดขึ้นจริงใน Eclipse)


java.class.pathสามารถมีหลายค่า หนึ่งค่าเหล่านั้นจะให้ไดเรกทอรีหรือไฟล์ JAR ซึ่งเป็นที่ตั้งของคลาสปัจจุบัน แต่อันไหน
มาร์ควิสแห่ง Lorne

ฉันยืนยันฉันลองใช้วิธีแก้ไขปัญหาอื่น ๆ แต่ไม่เคยได้รับชื่อไฟล์ jar มันใช้งานได้ง่ายมาก! ขอบคุณ - +1
guillaume girod-vitouchkina

5

คำตอบอื่น ๆ ดูเหมือนจะชี้ไปที่ซอร์สโค้ดซึ่งเป็นตำแหน่งไฟล์ Jar ซึ่งไม่ใช่ไดเรกทอรี

ใช้

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

มันอาจเป็นไดเรกทอรีถ้าคุณกำลังโหลดคลาสของคุณจากระบบไฟล์แทนไฟล์ JAR เช่นเมื่อทำการดีบั๊ก
มาร์ควิสแห่ง Lorne

4

คำตอบที่เลือกข้างต้นจะไม่ทำงานหากคุณเรียกใช้ jar โดยคลิกที่คำตอบจากสภาพแวดล้อมเดสก์ท็อปของ Gnome (ไม่ใช่จากสคริปต์หรือเทอร์มินัลใด ๆ )

ฉันชอบที่วิธีต่อไปนี้ใช้ได้ทุกที่:

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

2
คุณลองใช้แอปเพล็ตหรือแอพ เปิดตัวโดยใช้ Java Web Start? ความเข้าใจของฉันคือว่ามันจะล้มเหลวในทั้งสองสถานการณ์ (แม้ว่าแอพนั้นเชื่อถือได้)
Andrew Thompson

โซลูชันนี้สามารถส่งคืนตำแหน่ง "ได้" เท่านั้น ภายในไฟล์ JAR ไม่ใช่ตำแหน่งของไฟล์ JAR
มาร์ควิสแห่ง Lorne

ระวัง: ไม่แนะนำให้ใช้URLDecoderในการถอดรหัสอักขระพิเศษ โดยเฉพาะอย่างยิ่งตัวละครที่ชอบ+จะถูกถอดรหัสไปยังช่องว่างอย่างผิดพลาด ดูคำตอบของฉันสำหรับรายละเอียด
ctrueden

ในการบู๊ตสปริงมันจะทำการขว้าง NullPointerException
Ravi Parekh

คุณจะได้รับNPEหากไม่มีทรัพยากรใน JAR
WhiteAngel

3

จริงๆแล้วที่นี่เป็นรุ่นที่ดีกว่า - อันเก่าล้มเหลวหากชื่อโฟลเดอร์มีที่ว่างอยู่

  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 มากนัก แต่การจัดการไฟล์ในเครื่องอาจเป็นไปไม่ได้ที่จะดาวน์โหลดแอพ


มีหลายวิธีในการถอดรหัสเส้นทาง ไม่จำเป็นต้องเขียนรหัสของคุณเอง
มาร์ควิสแห่ง Lorne

3

ฉันพยายามใช้ jar ที่ใช้เส้นทาง

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

c: \ app> java -jar application.jar

ใช้แอปพลิเคชัน jar ชื่อ "application.jar" บน Windows ในโฟลเดอร์ " c: \ app " ค่าของตัวแปร String "folder" คือ " \ c: \ app \ application.jar " และฉันประสบปัญหาในการทดสอบ ความถูกต้องของเส้นทาง

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

ดังนั้นฉันจึงพยายามกำหนด "ทดสอบ" เป็น:

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

เพื่อให้ได้เส้นทางในรูปแบบที่ถูกต้องเช่น " c: \ app " แทนที่จะเป็น " \ c: \ app \ application.jar " และฉันสังเกตเห็นว่ามันใช้งานได้


3

ทางออกที่ง่ายที่สุดคือการส่งผ่านเส้นทางเป็นอาร์กิวเมนต์เมื่อใช้ขวด

คุณสามารถทำสิ่งนี้โดยอัตโนมัติด้วยเชลล์สคริปต์ (.bat ใน Windows, .sh ที่ใดก็ได้):

java -jar my-jar.jar .

ฉันเคย.ผ่านไดเรกทอรีการทำงานปัจจุบัน

UPDATE

คุณอาจต้องการติดไฟล์ jar ในไดเรกทอรีย่อยดังนั้นผู้ใช้จะไม่คลิกมันโดยไม่ตั้งใจ รหัสของคุณควรตรวจสอบเพื่อให้แน่ใจว่ามีการระบุอาร์กิวเมนต์บรรทัดคำสั่งและให้ข้อความแสดงข้อผิดพลาดที่ดีหากอาร์กิวเมนต์หายไป


3

ฉันต้องยุ่งมากก่อนที่ฉันจะพบวิธีแก้ปัญหา (และสั้น) ในที่สุด
มันเป็นไปได้ว่าjarLocationมาพร้อมกับคำนำหน้าเหมือนfile:\หรือซึ่งสามารถถอดออกได้โดยใช้jar:file\String#substring()

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

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

พา ธ อ้างอิงถึงทรัพยากรภายในไฟล์ jar เสมอ


1
สตริงเส้นทางนั้นยังคงต้องทำให้ง่ายขึ้นตามความต้องการของคุณ String path = new File(getClass().getResource("").getPath()).getParentFile().getParent(); File jarDir = new File(path.substring(5));
ZZZ

4
ทั้งสองgetResource("")และgetResource(".")ล้มเหลวในการทดสอบของฉันเมื่อเรียนอยู่ในไฟล์ JAR; การร้องขอทั้งสองคืนค่า null
ctrueden

2
NullPointerExceptionนี้พ่น
มาร์ควิสแห่ง 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;}

ทำงานได้ดีบน Windows


1

สิ่งที่น่าผิดหวังคือเมื่อคุณพัฒนาใน Eclipse MyClass.class.getProtectionDomain().getCodeSource().getLocation()จะส่งคืน/binไดเรกทอรีที่ยอดเยี่ยม แต่เมื่อคุณคอมไพล์ไปที่ jar เส้นทางจะมี/myjarname.jarส่วนที่ทำให้ชื่อไฟล์ผิดกฎหมาย

เพื่อให้โค้ดทำงานได้ทั้งใน IDE และเมื่อคอมไพล์ไปยัง jar ฉันใช้โค้ดต่อไปนี้:

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

ไม่แน่ใจเกี่ยวกับคนอื่น ๆ แต่ในกรณีของฉันมันไม่ได้ทำงานกับ "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");

1

ลองใช้วิธีแก้ปัญหาหลายวิธี แต่ไม่พบผลลัพธ์ที่ถูกต้องสำหรับกรณี (อาจเป็นพิเศษ) ที่ jar ที่รันได้ถูกเอ็กซ์พอร์ตด้วย "Packaging ภายนอกไลบรารี" ใน 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);


0

เมธอดนี้เรียกว่าจากรหัสในไฟล์เก็บถาวรส่งคืนโฟลเดอร์ที่ไฟล์. 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


3
"ควรทำงานได้ทั้ง Windows หรือ Unix" แต่จะล้มเหลวในแอปเพล็ตและทุกแอป เปิดตัวโดยใช้ JWS
Andrew Thompson

0

พูดถึงว่ามีการตรวจสอบเฉพาะในWindowsแต่ฉันคิดว่ามันทำงานได้ดีในระบบปฏิบัติการอื่น ๆ [ Linux,MacOs,Solaris] :)


ฉันมี2 .jarไฟล์ในไดเรกทอรีเดียวกัน ฉันต้องการจาก.jarไฟล์หนึ่งเพื่อเริ่มต้นอื่น.jarไฟล์ซึ่งอยู่ในไดเรกทอรีเดียวกัน

ปัญหาคือว่าเมื่อคุณเริ่มต้นจากไดเรกทอรีปัจจุบันคือcmdsystem32


คำเตือน!

  • ด้านล่างดูเหมือนว่าจะทำงานได้ดีในทุกการทดสอบที่ฉันทำแม้จะมีชื่อโฟลเดอร์;][[;'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'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

รหัสนี้เหมาะกับฉัน:

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

รหัสนี้ทำงานสำหรับฉันเพื่อระบุว่าโปรแกรมจะถูกดำเนินการภายในไฟล์ JAR หรือ 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;
    }
}

หากฉันต้องการเส้นทางแบบเต็มของไฟล์ JAR ของ Windows ฉันใช้วิธีนี้:

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

รหัสที่สมบูรณ์ของฉันทำงานกับแอพพลิเคชั่น Spring Boot โดยใช้CommandLineRunnerการติดตั้งเพื่อให้แน่ใจว่าแอปพลิเคชันจะถูกดำเนินการภายในมุมมองคอนโซล (ดับเบิลคลิกโดยไม่ตั้งใจในชื่อไฟล์ JAR) ฉันกำลังใช้รหัสถัดไป:

@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

ฉันเขียนใน 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 ที่เก่ากว่า


-1

getProtectionDomainวิธีการอาจจะไม่ทำงานในบางครั้งเช่นเมื่อคุณต้องไปหาขวดสำหรับบางส่วนของการเรียนภาษาจาวาหลัก (เช่นในกรณีของฉัน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, "");
}

URL.getPath () ไม่ทำสิ่งที่คุณคิด อักขระพิเศษใด ๆ จะถูกเข้ารหัสเป็นเปอร์เซ็นต์
VGR

-1

ฉันมีวิธีอื่นในการรับตำแหน่ง String ของคลาส

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

สตริงเอาต์พุตจะมีรูปแบบของ

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

ช่องว่างและตัวละครอื่น ๆ file:/ได้รับการจัดการและในรูปแบบโดยไม่ต้อง ดังนั้นจะง่ายต่อการใช้งาน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.