Einbinden von Bildern in ausführbare Jar-Datei

Goldinho

Aktives Mitglied
Hallo zusammen.

Ich möchte, dass die von meiner ausführbare JAR-Datei benutzten Bilder in die Datei eingebunden werden. Dazu habe ich nach einigen Recherchen sowohl einen Ordner images unter src erstellt als auch einen Build-Path.

Das eigentlich Problem scheint -wie ich hier und in anderen Foren gelesen habe- darin zu liegen, dass ich in meinem Code das Bild als Ressource laden muss. Also habe ich meinen ImageLoader umgeschrieben zu folgendem Code, der allerdings nicht funktioniert, sondern eine Exception wirft mit input == null.
Wo genau das Problem liegt, verstehe ich nicht. Kann mir jemand helfen?

LG Christian
-------------------------

Java:
package xy;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

public class ImageLoader {
   
    public BufferedImage image;
   
    public ImageLoader(String url) {
       
        try {
            image = ImageIO.read(getClass().getResource(url));
            }
       
        catch (IOException e) {
            e.printStackTrace();
            }
    }
}
 
Zuletzt bearbeitet von einem Moderator:
Schau mal in Dein Jar (das ist ein ZIP-File), wo genau Deine Bilder liegen und dann schreibst Du uns noch, was Du als URL zum Laden der Bilder übergibst.
 
Ich übergebe exakt den gleichen Pfad, der mit

image = ImageIO.read(new File(url));

(statt: image = ImageIO.read(getClass().getResource(url)); )

problemlos funktioniert.

Obiges Beispiel funktioniert ja nicht einmal aus Eclipse heraus.

Gruß
 
Herzlichen Dank. Wie gesagt, mit ersterem Beispiel geht es problemlos, folglich muss der Pfad ja passen. Und nachdem ich "Pfad" schrieb, war auch klar, dass ich weiß, was ein Pfad ist.
 
Herzlichen Dank. Wie gesagt, mit ersterem Beispiel geht es problemlos, folglich muss der Pfad ja passen. Und nachdem ich "Pfad" schrieb, war auch klar, dass ich weiß, was ein Pfad ist.
Nein. Er passt eben nicht. Im Jar sehen die Pfade meist anders aus als auf der Platte. Da dort die Pfade *nur* innerhalb des Jahrs beschrieben werden. In der Regel muss man die URL anpassen.
 
Ihr wollt mir also erzählen, dass IN ECLIPSE das Laden von Bildern bei der Übergabe des gleichen Strings in url einmal mit

image = ImageIO.read(new File(url));

wunderbar funktioniert und ich bei

image = ImageIO.read(getClass().getResource(url));

den Pfad anpassen muss?
 
Natürlich. Ist ja ein ganz anderes Dateisystem.

Wenn in Url z.B. "c:\" drinsteht, funktioniert das bei der Resource nicht mehr - wohl aber bei new File.
 
Also ich fasse mich einmal kurz:
a) Eclipse ist nur eine IDE. Wie kommst Du auf das schmale Brett, dass es da groß Einfluss auf eine Java Applikation geben sollte? Die Java Applikation läuft mit dem Java Framework unabhängig von der IDE. Die IDE automatisiert nur ein paar Dinge für Dich oder nimmt Dir etwas arbeit ab ...
b) Schau Dir doch einfach einmal die Dokumentation der Funktionen an. Für das getResource wäre das z.B. https://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getResource(java.lang.String)
 
Ihr wollt mir also erzählen, dass IN ECLIPSE das Laden von Bildern bei der Übergabe des gleichen Strings in url einmal mit
Ja. Wenn Du in der IDE auf Run drückst, wird Dein Programm üblicherweise aus dem Projektverzeichnis heraus gestartet. Dort steht Dir dann z. B. src/images/abc.png zur Verfügung. Beim Laden über den ClassLoader interessiert das Dateisystem aber nicht, sondern der Classpath. Zu diesem gehört dann wenigstens src nicht dazu.
 
Ich habe die Pfadangabe relativ gewählt, beginnend bei src

String fs = System.getProperty("file.separator");
String path = "src"+fs+"Paketname"+fs+"images"+fs;
String pic = "bild.png";
String url = path + pic;

Wie finde ich heraus, welchen Pfad jetzt getRessource möchte? Aus der Doku werd ich auch nicht ganz schlau.
 
Kurz und knapp, so ist es falsch. Den File Seperator ist für Jars auch falsch.

src brauchst du bei der Resource nicht. in der Regel wäre dein Pfad für die Datei "/paket/images/bild.png" mit führendem / am Anfang.

Schau im Zweifelsfall in das Jar rein, ist ja nur ein Zip-File, wo da das Bild liegt.
 
Ich habe jetzt alle Möglichkeiten einmal durchprobiert, beginnend bei 'images/bild.jpg' bis hin zu '/src/Paketname/images/bild.jpg'. Mit und ohne Slash am Anfang. Es kam immer der gleiche Fehler.
 
Ich glaube, ihr versteht mich falsch. Ich habe ja noch nicht mal ein Jar erstellt. Der Fehler tritt ja noch in der Entwicklungsumgebung auf.
 
Weiteres Problem ist, dass beim Export als Runnable Jar auch keine Bilder in der Datei gespeichert werden. Ich kriege immer nur eine einzelne Jar-Datei, egal ob ich 'Package', 'Extract' oder 'zusätzlicher Ordner' angebe. Muss ich da noch irgendwas einstellen?
 
Ich glaube, ihr versteht mich falsch. Ich habe ja noch nicht mal ein Jar erstellt. Der Fehler tritt ja noch in der Entwicklungsumgebung auf.
Ich wiederhole mich (s. #10): wenn über den ClassLoader eine Ressource geladen wird, steht das Dateisystem nicht zur Verfügung. Den Ordner "src" gibt es dort nicht. Der Pfad für den ClassLoader wäre z. B. "/strayDogs/images/auto.gif"

Weiteres Problem ist, dass beim Export als Runnable Jar auch keine Bilder in der Datei gespeichert werden. Ich kriege immer nur eine einzelne Jar-Datei,
Der letzte Halbsatz gibt mir Rätsel auf: hast Du die Jar-Datei entpackt?
 
Ich wiederhole mich ebenfalls: ich habe das schon getestet. Jetzt noch einmal mit genau deinem Vorschlag. Im Screenshot siehst du, was als URL übergeben wurde, samt Fehlermeldung und dazugehörigem Dateisystem. Der Fehler muss woanders liegen.
12613



------ Der letzte Halbsatz gibt mir Rätsel auf: hast Du die Jar-Datei entpackt?

Nein. Denn nachdem ich im links zu sehenden Eclipse-Dateisystem mit der rechten Maustatse auf mein Package gehe, 'Runnable Jar' wähle, anschließend den Radiobutton 'Packacke required libraries into generated Jar' setze, erhalte ich auf dem Desktop eine einzelne Jar-Datei ohne alles weitere. Keine ZIP-Datei. Eine einzelne JAR-Datei.
 
So, jetzt hab ichs. Ich habe jetzt noch einmal alle möglichen Pfadangaben durchprobiert und tatsächlich: Der ClassLoader wollte lediglich einen Slash und den Bildnamen haben, sonst nichts. Kann mir das jemand erklären?
Liegt es eventuell daran, dass ich in Eclipse schon den Pfad zum Ressourcen-Ordner eingestellt habe?

Wenn ich die Jar-Datei jetzt erstellen und laufen lasse, krieg ich auch endlich meine Bildchen angezeigt, ohne dass die Jar-Datei mit meinem Bilderordner zusammen liegen muss.

Schwere Geburt, aber danke. Letztendlich habt ihr ja schon den richtigen Tipp gegeben. Tut mir leid, wenn ich etwas mürrisch war, sitz auch schon ne Weile an dem Mist 😉 Wirklich verstanden hab ichs zwar noch nicht, aber das kommt vielleicht noch.
 
So, jetzt hab ichs. Ich habe jetzt noch einmal alle möglichen Pfadangaben durchprobiert und tatsächlich: Der ClassLoader wollte lediglich einen Slash und den Bildnamen haben, sonst nichts. Kann mir das jemand erklären?
Liegt es eventuell daran, dass ich in Eclipse schon den Pfad zum Ressourcen-Ordner eingestellt habe?

Wenn ich die Jar-Datei jetzt erstellen und laufen lasse, krieg ich auch endlich meine Bildchen angezeigt, ohne dass die Jar-Datei mit meinem Bilderordner zusammen liegen muss.

Schwere Geburt, aber danke. Letztendlich habt ihr ja schon den richtigen Tipp gegeben. Tut mir leid, wenn ich etwas mürrisch war, sitz auch schon ne Weile an dem Mist 😉 Wirklich verstanden hab ichs zwar noch nicht, aber das kommt vielleicht noch.
Ich versuche es mal zu erklären. Die Ursache hast du vermutlich schon genannt.

Du kannst in das Jar rein schauen (einfach mal nach .zip umbennen) und dir die Ordnerstruktur ansehen. Dann wird es vermutlich klarer.

Alles, was du als Resourcen & Co einstellst wird im Jar zusammengepackt. Wenn du also "src/strayDogs" als Resourcen-Folder angegben hast, dann wird alles *unterhalb* dieser Ebene entsprechend der darunterliegenden Struktur in das Jar gepackt. Referenzieren tust dann nur die Ebenen darunter - alles darüber ist im Jar nicht mehr sichtbar.

Das gleiche gilt, wenn du es z.B. aus Eclipse und Co ausführst. Denn eigentlich ist es auch egal wie es im Jar zusammengepackt ist, relevant ist, wie java den Classpath zusammenpackt - wo es also sucht wenn du z.B. die Klasse org.example.ExampleClass referenziert. Beim Jar File mappt das normalerweise nur 1:1 auf den Classpath, innerhalb der Entwicklungsumgebung sieht man das ggf. nicht so deutlich.
 
Hallo und herzlichen Dank an die Spezialisten hier. Die ausführlich beschriebene Lösung des Problems von Goldinho hat auch mir sehr geholfen.

Eine Frage hätte ich allerdings noch. Jetzt werden ja bei der Erzeugung der JAR-Datei alle Bilddateien in die JAR-Datei geladen. Der dafür zuständige Code wird aber weiterhin bei jedem Programmstart ausgeführt. Ok, wenn die Resourcenquelle dabei nicht gefunden wird, können auch keine Dateien geladen werden. Was ist aber, wenn die JAR-Datei, warum auch immer, auf den Resourcenordner zugreifen kann, wie verhält sich in einem solchen Fall das Programm?

Gruß, kodela
 
Also Du musst das zweigeteilt sehen:
a) Übersetzung / Bauen des JAR Files. Das läuft einmalig bei den Entwicklern. Da passieren dann folgende Schritte:
- Java Dateien werden zu .class Dateien übersetzt.
- Ressourcen werden kopiert
- ggf. werden Tests ausgeführt
- Am Ende wird das jar File gebaut - welches wie @LimDul erläuterte, eigentlich eine Art ZIP Datei ist.
==> Das Laden der Bilddaten in die JAR Datei erfolgt somit nur bei der Erstellung der JAR Datei. Nicht bei Ausführung von eben dieser!

b) Nutzung der Applikation
Hier ist das jar Datei immer unverändert. Der Java Prozess lädt dann Teile aus der jar Datei. So muss z.B, die Klasse mit der main Methode geladen werden. Und so niemand die jar Datei mutwillig verändert, sind natürlich auch die Ressourcen da noch drin.
Somit sollte es nie den Fall geben, dass die Ressourcen nicht mehr vorhanden sind. Das wäre ein Zeichen für eine korrupte oder fehlerhafte JAR Datei. (Das hoffentlich vor einer Weitergabe immer auffallen würde. Gewisse Tests des fertigen Produkts sollten ja schon sein 🙂 )

Und ganz wichtig: Die JAR Datei später greift nicht auf den Ordner selbst zu. Das geben manche Tutorials auch falsch wieder und da hatte ich in der Vergangenheit schon kleine Diskussionen hier. Bei a) kommen die Daten natürlich aus den entsprechenden Ordnern. Im Rahmen des Build Prozesses werden Dateien kopiert und auch Archive / jar Files zusammen gepackt. Aber das Programm greift dann da natürlich nicht mehr drauf zu! Du musst also keinen Ordner von Deinem Projekt weiter geben. So alles in der jar Datei drin ist (Ein sogenanntes fat jar) musst Du nur das jar Datei weiter geben. (Wenn Du natürlich noch externe Abhängigkeiten hast, dann musst Du auch externe Abhängigkeiten mit geben, aber die Bilder sind ja in die ja Datei kopiert worden. Daher sind das keine externen Abhängigkeiten mehr!)

Konnte ich das etwas klären? Oder sind immer noch Punkte unklar?
 
Danke für die ausführliche Antwort. Und, es ist eigentlich alles klar.

Hintergrund meiner Frage war der Umstand, dass seit Einbindung der Bild-Resourcen (36 PNG-Dateien im Format 54x54) beim Start des Programms ein kurzes weißes Aufblitzen erfolgt. Ich habe (im vergangenen Jahrhundert) das gleiche Programm (Schach) in C++ geschrieben. Rein aus Interesse übersetze ich es zu Java. Dieses Uralt-Programm startet mit einem Wimpernschlag. Das Java-Pendant sieht da lange nicht so gut aus. Das gilt aber nur für den Fall, dass mehrere Resourcen gespeichert werden.
 
Danke für die ausführliche Antwort. Und, es ist eigentlich alles klar.

Hintergrund meiner Frage war der Umstand, dass seit Einbindung der Bild-Resourcen (36 PNG-Dateien im Format 54x54) beim Start des Programms ein kurzes weißes Aufblitzen erfolgt. Ich habe (im vergangenen Jahrhundert) das gleiche Programm (Schach) in C++ geschrieben. Rein aus Interesse übersetze ich es zu Java. Dieses Uralt-Programm startet mit einem Wimpernschlag. Das Java-Pendant sieht da lange nicht so gut aus. Das gilt aber nur für den Fall, dass mehrere Resourcen gespeichert werden.
Ich vermute - dafür müsste man aber tiefer reinschauen, dass man das Laden mit Sicherheit "intelligenter" machen kann. Java und Gui kann manchmal tricky sein, insbesondere was man wann tut. Da gibt es einige Fallstricke. In Swing gilt:
- Gui Änderungen nur im Event Dispatching Thread
- Im Event Dispatching Thread nichts komplexes, sonst friert die GUI ein während der Operation

Und das unter einen Hut zu bringen und ggf. zu synchronisieren ist zwar nicht wirklich schwer, wenn man es einmal komplett verinnerlicht hat - aber wenn man sich da nicht auskennt läuft man leicht in Fallstricke rein.
 

Neue Themen


Zurück
Oben