Java JAR dosyasındaki bir kaynağa giden yolu alma


166

Bir Kaynağa giden yolu bulmaya çalışıyorum ama şansım olmadı.

Bu çalışır (hem IDE ve JAR ile) ama bu şekilde bir dosyaya yol alamıyorum, sadece dosya içeriği:

ClassLoader classLoader = getClass().getClassLoader();
PrintInputStream(classLoader.getResourceAsStream("config/netclient.p"));

Bunu yaparsam:

ClassLoader classLoader = getClass().getClassLoader();
File file = new File(classLoader.getResource("config/netclient.p").getFile());

Sonuç: java.io.FileNotFoundException: file:/path/to/jarfile/bot.jar!/config/netclient.p (No such file or directory)

Bir kaynak dosyasının yolunu almanın bir yolu var mı?


1
Evet. Ben her ikisi ile çalışmak istiyorum bir sınıf var, dış (bir yapılandırma dosyasının bazı parametresini değiştirmek istiyorum durumunda) bir klasör ve uygulama yapılandırma dosyalarını kullanıcıya (bir dağıtılabilir gibi gizler) bir JAR var Tüm insanlara JAR).
no_ripcord

1
Böylece sınıf sadece bir dosyaya (config dosyası) bir PATH alır.
no_ripcord

3
O zaman muhtemelen bu sınıfın her iki kaynaktan da alabileceğiniz bir girdi akışı ile ilgilenmesi gerekir.
Carl Manaster

1
Evet biliyorum. Ama başka türlü daha açık ve temiz olurdu. Ama yine de teşekkürler.
no_ripcord

1
Bu soruda 4. seçenek gibi bir şey yapmaya mı çalışıyorsunuz? stackoverflow.com/questions/775389/…
erickson

Yanıtlar:


71

Bu kasıtlı. "Dosyanın" içeriği dosya olarak mevcut olmayabilir. Bir JAR dosyasının veya başka bir tür kaynağın parçası olabilecek sınıflar ve kaynaklarla uğraştığınızı unutmayın. Sınıf yükleyicinin kaynağa bir dosya tanıtıcısı sağlaması gerekmez, örneğin jar dosyası dosya sistemindeki tek tek dosyalara genişletilmemiş olabilir.

Bir java.io.File dosyası alarak yapabileceğiniz her şey, bir java.io.File dosyası kesinlikle gerekliyse, akışı geçici bir dosyaya kopyalayarak ve aynısını yaparak yapılabilir.


6
kaynağınızı açmak için aradığınızda 'rsrc:' ekleyebilirsiniz. yeni Dosya gibi ("rsrc: dosyaadı.txt") bu, kavanozunuzun köküne paketlenmiş dosyaadı.txt
dosyasını yükler

63

Bir kaynak yüklerken aşağıdakiler arasındaki farkı fark ettiğinizden emin olun:

getClass().getClassLoader().getResource("com/myorg/foo.jpg") //relative path

ve

getClass().getResource("/com/myorg/foo.jpg")); //note the slash at the beginning

Sanırım, bu karışıklık bir kaynak yüklerken sorunların çoğuna neden oluyor.


Ayrıca, bir resim yüklerken kullanımı daha kolaydır getResourceAsStream():

BufferedImage image = ImageIO.read(getClass().getResourceAsStream("/com/myorg/foo.jpg"));

Gerçekten bir JAR arşivinden (resim olmayan) bir dosya yüklemeniz gerektiğinde şunu deneyebilirsiniz:

File file = null;
String resource = "/com/myorg/foo.xml";
URL res = getClass().getResource(resource);
if (res.getProtocol().equals("jar")) {
    try {
        InputStream input = getClass().getResourceAsStream(resource);
        file = File.createTempFile("tempfile", ".tmp");
        OutputStream out = new FileOutputStream(file);
        int read;
        byte[] bytes = new byte[1024];

        while ((read = input.read(bytes)) != -1) {
            out.write(bytes, 0, read);
        }
        out.close();
        file.deleteOnExit();
    } catch (IOException ex) {
        Exceptions.printStackTrace(ex);
    }
} else {
    //this will probably work in your IDE, but not from a JAR
    file = new File(res.getFile());
}

if (file != null && !file.exists()) {
    throw new RuntimeException("Error: File " + file + " not found!");
}

3
+1 Bu benim için çalıştı. Okumak istediğiniz dosyaları binklasöre yerleştirdiğinizden ve `/com/myorg/filename.ext 'yolunu kullanmadan önce kaynaklara yüklenen sınıfın dizinine gittiğinizden emin olun .
rayryeng

+1 Bu benim için de işe yarıyor ... Bu yaklaşımla ilgili bazı güvenlik riskleri olabileceğini anlıyorum, bu nedenle uygulama sahiplerinin bunun farkında olması gerekiyor.
LucasA

"getResourceAsStream () ile bir kaynak yüklemek her zaman daha iyidir" diyebilir misiniz? Bu soruna nasıl bir çözüm sağlayabilir?
Luca S.

@LucaS. Bu sadece görüntüler içindi, üzgünüm çok net değildi. Yaklaşım platformdan bağımsız olmalı, ancak biraz acayip bir yol.
Tombart

@LucasA Bahsettiğiniz riskleri açıklığa kavuşturabilir misiniz?
ZX9

25

Bir satır cevap -

String path = this.getClass().getClassLoader().getResource(<resourceFileName>).toExternalForm()

Temel olarak getResourceyöntem URL'yi verir. Bu URL'den yolu arayaraktoExternalForm()

Referanslar:

getResource () , toExternalForm ()


7
Ortamımdan (IntelliJ) çalışırken bu, her durumda çalışan basit bir dosya URL'si üretir. Ancak, kavanozun kendisinden çalışırken, jar: file: /path/to/jar/jarname.jar! /File_in_jar.mp4 benzeri bir URI alıyorum. Her şey kavanozla başlayan bir URI'yi kullanamaz. JavaFX Ortamında örnek olay.
Noah Ternullo

1
Bu cevabı en çok seviyorum. Çoğu durumda, bir jar dosyasındayken kaynağa InputStream'i almanız tercih edilebilir, ancak herhangi bir nedenden dolayı yola gerçekten ihtiyacınız varsa, bu işe yarar. Üçüncü taraf bir nesneye verilecek yola ihtiyacım vardı. Çok teşekkürler!
Mario

1
Yukarıdaki çözüm file:/
IDE'deyken çalışmaz

Yanıtınız en az 2 gündür üzerinde çalışmaya çalıştığım bir sorunu çözdü. TYVM !!
Jonathan

12

Bu sorunla uğraşmak için biraz zaman geçirdim, çünkü bulduğum hiçbir çözüm garip bir şekilde çalıştı! Çalışma dizini genellikle JAR'ın dizini değildir, özellikle de Windows altında Başlat Menüsünden bir JAR (veya herhangi bir program) çalıştırılıyorsa. İşte yaptım ve bir JAR dışında çalışan .class dosyaları için çalışıyor ve bir JAR için çalışıyor. (Sadece Windows 7 altında test ettim.)

try {
    //Attempt to get the path of the actual JAR file, because the working directory is frequently not where the file is.
    //Example: file:/D:/all/Java/TitanWaterworks/TitanWaterworks-en.jar!/TitanWaterworks.class
    //Another example: /D:/all/Java/TitanWaterworks/TitanWaterworks.class
    PROGRAM_DIRECTORY = getClass().getClassLoader().getResource("TitanWaterworks.class").getPath(); // Gets the path of the class or jar.

    //Find the last ! and cut it off at that location. If this isn't being run from a jar, there is no !, so it'll cause an exception, which is fine.
    try {
        PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(0, PROGRAM_DIRECTORY.lastIndexOf('!'));
    } catch (Exception e) { }

    //Find the last / and cut it off at that location.
    PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(0, PROGRAM_DIRECTORY.lastIndexOf('/') + 1);
    //If it starts with /, cut it off.
    if (PROGRAM_DIRECTORY.startsWith("/")) PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(1, PROGRAM_DIRECTORY.length());
    //If it starts with file:/, cut that off, too.
    if (PROGRAM_DIRECTORY.startsWith("file:/")) PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(6, PROGRAM_DIRECTORY.length());
} catch (Exception e) {
    PROGRAM_DIRECTORY = ""; //Current working directory instead.
}

8

eğer netclient.pbir JAR dosyası içinde kalacak dosyası diğer dosyada içinde bulunması nedeniyle, bu bir yol olmayacaktır. bu durumda, sahip olabileceğiniz en iyi yol gerçekten file:/path/to/jarfile/bot.jar!/config/netclient.p.


Bu biçimdeki bir URL'yi (... bot.jar! / Config / ...) URI'ye dönüştürmeye çalıştığımda yolun hiyerarşik olmadığını söylüyor.
gEdringer

7

Jar dosyası içindeki yolu anlamanız gerekir.
Sadece göreceli bakın. Yani dizinin altında foo.jar dosyasında \src\main\resources(maven stili) bulunan bir dosyanız varsa (dosyam.txt ). Buna şöyle bakabilirsiniz:

src/main/resources/myfile.txt

Kullanarak jar -tvf myjar.jar kavanozunuzu dökerseniz, çıktıyı ve kavanoz dosyası içindeki göreceli yolu görürsünüz ve İLERİ SLAŞTIRMALARI kullanabilirsiniz.


Gerçekten, Windows'ta bile eğik çizgi kullanmanız gerekir. Bu, kullanamayacağınız anlamına gelir File.separator.
str

3

Benim durumumda, Yol yerine bir URL nesnesi kullandım.

Dosya

File file = new File("my_path");
URL url = file.toURI().toURL();

Classloader kullanarak sınıfyolunda kaynak

URL url = MyClass.class.getClassLoader().getResource("resource_name")

İçeriği okumak gerektiğinde, aşağıdaki kodu kullanabilirsiniz:

InputStream stream = url.openStream();

Ve bir InputStream kullanarak içeriğe erişebilirsiniz.


3

Bu, jar kaynağından geçici dosya içeriğinin eksik kopyalanmasını önlemek ve benzersiz geçici dosya adlarına sahip olmak için akışlı gömme ve yakın olan Tombart kullanıcısıyla aynı koddur.

File file = null;
String resource = "/view/Trial_main.html" ;
URL res = getClass().getResource(resource);
if (res.toString().startsWith("jar:")) {
    try {
        InputStream input = getClass().getResourceAsStream(resource);
        file = File.createTempFile(new Date().getTime()+"", ".html");
        OutputStream out = new FileOutputStream(file);
        int read;
        byte[] bytes = new byte[1024];

        while ((read = input.read(bytes)) != -1) {
            out.write(bytes, 0, read);
        }
        out.flush();
        out.close();
        input.close();
        file.deleteOnExit();
    } catch (IOException ex) {
        ex.printStackTrace();
    }
} else {
    //this will probably work in your IDE, but not from a JAR
    file = new File(res.getFile());
}
         

2

Dosya, dosya sistemindeki bir dosya için bir soyutlamadır ve dosya sistemleri JAR'ın içeriği hakkında hiçbir şey bilmez.

Bir URI ile deneyin, bence amaçlarınız için yararlı olabilecek bir jar: // protokolü var.


1

Aşağıdaki yol benim için çalıştı: classpath:/path/to/resource/in/jar


1
@Anatoly Yukarıdaki
verilerden

1
private static final String FILE_LOCATION = "com/input/file/somefile.txt";

//Method Body


InputStream invalidCharacterInputStream = URLClassLoader.getSystemResourceAsStream(FILE_LOCATION);

Bunu almak getSystemResourceAsStreamen iyi seçenektir. Dosya veya URL yerine giriş akışını alarak, bir JAR dosyasında ve tek başına çalışır.



0

Bir jar dosyasındayken, kaynak kesinlikle paket hiyerarşisinde bulunur (dosya sistemi hiyerarşisinde değil). Bu nedenle, "./default.conf" adlı bir kaynağı yükleyen com.example.Sweet sınıfınız varsa, kaynağın adı "/com/example/default.conf" olarak belirtilir.

Ama eğer bir kavanozda ise o zaman bir Dosya değil ...


0

Kavanozunuzun ressources klasörünüzün (java / main / resources) içine dosyanızı ekleyin ( imports.xml adlı bir xml dosyası eklediğinizi varsayarız ), bundan sonra ResourceLoaderferyat gibi yay kullanırsanız enjekte edersiniz

@Autowired
private ResourceLoader resourceLoader;

tur fonksiyonu içinde dosyayı yüklemek için feryat kodu yazın:

    Resource resource = resourceLoader.getResource("classpath:imports.xml");
    try{
        File file;
        file = resource.getFile();//will load the file
...
    }catch(IOException e){e.printStackTrace();}

0

Belki bu yöntem hızlı çözüm için kullanılabilir.

public class TestUtility
{ 
    public static File getInternalResource(String relativePath)
    {
        File resourceFile = null;
        URL location = TestUtility.class.getProtectionDomain().getCodeSource().getLocation();
        String codeLocation = location.toString();
        try{
            if (codeLocation.endsWith(".jar"){
                //Call from jar
                Path path = Paths.get(location.toURI()).resolve("../classes/" + relativePath).normalize();
                resourceFile = path.toFile();
            }else{
                //Call from IDE
                resourceFile = new File(TestUtility.class.getClassLoader().getResource(relativePath).getPath());
            }
        }catch(URISyntaxException ex){
            ex.printStackTrace();
        }
        return resourceFile;
    }
}

Biraz bağlam atlıyor musunuz? Bu bana verir:java.lang.NullPointerException: Attempt to invoke virtual method 'java.security.CodeSource java.security.ProtectionDomain.getCodeSource()' on a null object reference
Allen Luce

Cevabı düzenledim ve bir yazılımda kullandığım tüm yöntemi yazdım
gbii

0

kodu takip et!

/ Src / main / resources / dosya

streamToFile(getClass().getClassLoader().getResourceAsStream("file"))

public static File streamToFile(InputStream in) {
    if (in == null) {
        return null;
    }

    try {
        File f = File.createTempFile(String.valueOf(in.hashCode()), ".tmp");
        f.deleteOnExit();

        FileOutputStream out = new FileOutputStream(f);
        byte[] buffer = new byte[1024];

        int bytesRead;
        while ((bytesRead = in.read(buffer)) != -1) {
            out.write(buffer, 0, bytesRead);
        }

        return f;
    } catch (IOException e) {
        LOGGER.error(e.getMessage(), e);
        return null;
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.