So erhalten Sie einen Pfad zu einer Ressource in einer Java-JAR-Datei

166

Ich versuche, einen Weg zu einer Ressource zu finden, aber ich hatte kein Glück.

Dies funktioniert (sowohl in der IDE als auch mit der JAR), aber auf diese Weise kann ich keinen Pfad zu einer Datei erhalten, sondern nur den Dateiinhalt:

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

Wenn ich das mache:

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

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

Gibt es eine Möglichkeit, einen Pfad zu einer Ressourcendatei zu erhalten?

no_ripcord
quelle
1
Ja. Ich habe eine Klasse, mit der ich arbeiten möchte, einen Ordner an der Außenseite (falls ich einen Parameter der Konfigurationsdatei ändern möchte) und eine JAR, die die Implementierungskonfigurationsdateien für den Benutzer verbirgt (wie eine verteilbare Datei) JAR an alle Leute).
no_ripcord
1
Die Klasse erhält also nur einen PFAD zu einer Datei (der Konfigurationsdatei).
no_ripcord
3
Dann sollte sich diese Klasse wahrscheinlich mit einem Eingabestream befassen, den Sie von beiden Quellen erhalten können.
Carl Manaster
1
Ja, ich weiß. Aber andersherum wäre es klarer und sauberer gewesen. Aber trotzdem danke.
no_ripcord
1
Versuchen Sie in dieser Frage so etwas wie Option 4 zu tun? stackoverflow.com/questions/775389/…
erickson

Antworten:

70

Das ist absichtlich. Der Inhalt der "Datei" ist möglicherweise nicht als Datei verfügbar. Denken Sie daran, dass Sie es mit Klassen und Ressourcen zu tun haben, die Teil einer JAR-Datei oder einer anderen Art von Ressource sein können. Der Klassenladeprogramm muss der Ressource kein Dateihandle bereitstellen. Beispielsweise wurde die JAR-Datei möglicherweise nicht in einzelne Dateien im Dateisystem erweitert.

Alles, was Sie tun können, indem Sie eine java.io.File abrufen, können Sie tun, indem Sie den Stream in eine temporäre Datei kopieren und dasselbe tun, wenn eine java.io.File unbedingt erforderlich ist.

Neal Maloney
quelle
6
Sie können 'rsrc:' hinzufügen, wenn Sie Ihre Ressource aufrufen, um sie zu öffnen. wie neue Datei ( „rsrc: filename.txt“) diese filename.txt geladen werden kann, die im Inneren der Wurzel Ihrer jar gepackt
gipsh
63

Achten Sie beim Laden einer Ressource auf den Unterschied zwischen:

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

und

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

Ich denke, diese Verwirrung verursacht die meisten Probleme beim Laden einer Ressource.


Wenn Sie ein Bild laden, ist es außerdem einfacher zu verwenden getResourceAsStream():

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

Wenn Sie wirklich eine (Nicht-Image-) Datei aus einem JAR-Archiv laden müssen, können Sie Folgendes versuchen:

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!");
}
Tombart
quelle
3
+1 Das hat bei mir funktioniert. Stellen Sie sicher, dass Sie die Dateien, die Sie einlesen möchten, in den binOrdner legen und in das Verzeichnis der Klasse wechseln, die in die Ressourcen geladen wird, bevor Sie den Pfad "/com/myorg/filename.ext" verwenden.
Rayryeng
+1 Dies funktioniert auch für mich ... Ich verstehe, dass mit diesem Ansatz einige Sicherheitsrisiken verbunden sein können, daher müssen Anwendungsbesitzer sich dessen bewusst sein.
LucasA
Könnten Sie diese Aussage klarstellen: "Es ist immer besser, eine Ressource mit getResourceAsStream () zu laden"? Wie kann dies eine Lösung für das Problem bieten?
Luca S.
@ LucaS. Das war nur für den Fall von Bildern gedacht, leider war es nicht sehr klar. Der Ansatz sollte plattformunabhängig sein, obwohl es ein bisschen hackig ist.
Tombart
@LucasA Könnten Sie die von Ihnen genannten Risiken klären?
ZX9
25

Die einzeilige Antwort lautet -

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

Grundsätzlich getResourcegibt Methode die URL an. Über diese URL können Sie den Pfad durch Aufrufen extrahierentoExternalForm()

Verweise:

getResource () , toExternalForm ()

Manojkumar Khotele
quelle
7
Während der Ausführung in meiner Umgebung (IntelliJ) wird eine einfache Datei-URL erstellt, die in allen Fällen funktioniert. Wenn ich jedoch aus dem JAR selbst laufe, erhalte ich einen URI ähnlich dem JAR: file: /path/to/jar/jarname.jar! /File_in_jar.mp4. Nicht alles kann eine URI verwenden, die mit jar beginnt. Ein typisches Beispiel für JavaFX Media.
Noah Ternullo
1
Diese Antwort gefällt mir am besten. Zugegeben, in den meisten Fällen ist es möglicherweise vorzuziehen, den InputStream einfach in die Ressource zu greifen, wenn er sich in einer JAR-Datei befindet. Wenn Sie jedoch aus irgendeinem Grund den Pfad wirklich benötigen, funktioniert dies. Ich brauchte den Pfad, um ihn einem Objekt eines Drittanbieters zu geben. So danke!
Mario
1
Die obige Lösung funktioniert nicht, während in IDE, in Intellijit fügt file:/Pfad hinzu, der in jar funktioniert, aber nicht in IDE
Tayab Hussain
Ihre Antwort löste ein Problem, das ich seit mindestens 2 Tagen zu lösen versuchte. TYVM !!
Jonathan
12

Ich habe eine Weile mit diesem Problem herumgespielt, weil seltsamerweise keine Lösung, die ich gefunden habe, tatsächlich funktioniert hat! Das Arbeitsverzeichnis ist häufig nicht das Verzeichnis der JAR, insbesondere wenn eine JAR (oder ein anderes Programm) über das Startmenü unter Windows ausgeführt wird. Also hier ist, was ich getan habe, und es funktioniert für .class-Dateien, die von außerhalb einer JAR ausgeführt werden, genauso wie es für eine JAR funktioniert. (Ich habe es nur unter Windows 7 getestet.)

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.
}
DeProgrammer
quelle
8

Wenn netclient.pes sich in einer JAR-Datei befindet, hat es keinen Pfad, da sich diese Datei in einer anderen Datei befindet. In diesem Fall ist der beste Weg, den Sie haben können, wirklich file:/path/to/jarfile/bot.jar!/config/netclient.p.

cd1
quelle
Wenn ich versuche, eine URL dieses Formats (... bot.jar! / Config / ...) in URI zu konvertieren, heißt es, dass der Pfad nicht hierarchisch ist.
gEdringer
7

Sie müssen den Pfad in der JAR-Datei verstehen.
Beziehen Sie sich einfach relativ darauf. Wenn Sie also eine Datei (myfile.txt) haben, die sich in foo.jar unter dem \src\main\resourcesVerzeichnis befindet (Maven-Stil). Sie würden sich darauf beziehen wie:

src/main/resources/myfile.txt

Wenn Sie Ihr JAR mit jar -tvf myjar.jar ausgeben, sehen Sie die Ausgabe und den relativen Pfad in der JAR-Datei und verwenden die FORWARD SLASHES.

Eric Manley
quelle
Sie müssen in der Tat Schrägstriche verwenden, auch unter Windows. Das bedeutet, dass Sie nicht verwenden können File.separator.
Str
3

In meinem Fall habe ich stattdessen ein URL-Objekt verwendet.

Datei

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

Ressource im Klassenpfad mit Classloader

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

Wenn ich den Inhalt lesen muss, kann ich den folgenden Code verwenden:

InputStream stream = url.openStream();

Und Sie können mit einem InputStream auf den Inhalt zugreifen.

jmgoyesc
quelle
3

Dies ist derselbe Code von Benutzer Tombart mit Stream Flush und Close, um eine unvollständige temporäre Kopie des Dateiinhalts aus der JAR-Ressource zu vermeiden und eindeutige temporäre Dateinamen zu erhalten.

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());
}
         
Bruce
quelle
2

Eine Datei ist eine Abstraktion für eine Datei in einem Dateisystem, und die Dateisysteme wissen nichts über den Inhalt einer JAR.

Versuchen Sie es mit einer URI. Ich denke, es gibt ein jar: // -Protokoll, das für Ihre Zwecke nützlich sein könnte.

fortran
quelle
1

Der folgende Weg hat bei mir funktioniert: classpath:/path/to/resource/in/jar

Anatoly
quelle
1
@Anatoly Könnten Sie bitte mehr Daten auf oben teilen
Kasun Siyambalapitiya
1
private static final String FILE_LOCATION = "com/input/file/somefile.txt";

//Method Body


InputStream invalidCharacterInputStream = URLClassLoader.getSystemResourceAsStream(FILE_LOCATION);

Dies getSystemResourceAsStreamist die beste Option. Durch Abrufen des Eingabestreams anstelle der Datei oder der URL funktioniert dies in einer JAR-Datei und als eigenständige Datei.

Shano
quelle
1

Es mag etwas spät sein, aber Sie können meine Bibliothek KResourceLoader verwenden , um eine Ressource aus Ihrem Glas zu erhalten:

File resource = getResource("file.txt")
Lamberto Basti
quelle
0

In einer JAR-Datei befindet sich die Ressource absolut in der Pakethierarchie (nicht in der Dateisystemhierarchie). Wenn Sie also die Klasse com.example.Sweet haben, die eine Ressource mit dem Namen "./default.conf" lädt, wird der Name der Ressource als "/com/example/default.conf" angegeben.

Aber wenn es in einem Glas ist, dann ist es keine Datei ...

Vilnis Krumins
quelle
0

Fügen Sie in Ihrem Ressourcenordner (java / main / resources) Ihres JAR Ihre Datei hinzu (wir gehen davon aus, dass Sie eine XML-Datei mit dem Namen imports.xml hinzugefügt haben ). Anschließend injizieren Sie, ResourceLoaderwenn Sie spring like bellow verwenden

@Autowired
private ResourceLoader resourceLoader;

Innerhalb der Tour-Funktion schreiben Sie den folgenden Code, um die Datei zu laden:

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

Vielleicht kann diese Methode für eine schnelle Lösung verwendet werden.

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;
    }
}
gbii
quelle
Lassen Sie einen Kontext aus? Dies gibt mir:java.lang.NullPointerException: Attempt to invoke virtual method 'java.security.CodeSource java.security.ProtectionDomain.getCodeSource()' on a null object reference
Allen Luce
Ich habe die Antwort bearbeitet und die gesamte Methode geschrieben, die ich in einer Software verwendet habe
gbii
0

Code folgen!

/ src / main / resources / file

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