javafx - load image from within a jar - java

I'm sorry for asking such a beginner question, but I just can't get it to work and I can't find the answer anywere either.
I want to have an image inside my .jar file and load it. While that sounds simple, I was only able to load an image while running from inside the IDE but not anymore after making the .jar (Thanks to google I was able to get the .png inside the .jar). Here is what I tried:
BorderPane bpMain = new BorderPane();
String fs = File.separator;
Image imgManikin;
try {
imgManikin = new Image(
Main.class.getProtectionDomain().getCodeSource().getLocation().toURI().toString()+"\\manikin.png");
bpMain.setBottom(new Label(Main.class.getProtectionDomain().getCodeSource().getLocation().toURI().toString()+"\\manikin.png"));
} catch (URISyntaxException e) {
imgManikin = new Image(
Main.class.getProtectionDomain().getCodeSource().getLocation().getPath()+"\\manikin.png");
System.out.println(Main.class.getProtectionDomain().getCodeSource().getLocation().getPath()+"\\manikin.png");
bpMain.setBottom(new Label(Main.class.getProtectionDomain().getCodeSource().getLocation().getPath()+"\\manikin.png"));
}
//Image imgManikin = new Image("file:src\\manikin.png");
ImageView imgvBackground = new ImageView(imgManikin);
imgvBackground.setFitWidth(100);
imgvBackground.setPreserveRatio(true);
bpMain.setCenter(imgvBackground);
primaryStage.setTitle("Kagami");
primaryStage.setScene(new Scene(bpMain, 300, 275));
primaryStage.show();
Needlessly to say it didn't work. It is showing me the Label at the bottom with the path just as intended, but it seams like the path just isn't right. (I also tried using the File.seperator instead of \\ and even /, but I got the same result every time: It showes me the path but won't load the image.
I'm using Windows 7, the IDE is IntelliJ and I have the newest Java update.

If the jar file is on the classpath of your application and the image to be loaded is located at the root of the jar file, the image can be loaded easily by:
URL url = getClass().getResource("/manikin.png");
BufferedImage awtImg = ImageIO.read(url);
Image fxImg = SwingFXUtils.toFxImage(awtImg, new Image());
Image fxImgDirect = new Image(url.openStream());
While ImageIO returns a BufferedImage this can be converted to a fx Image using the SwingUtils. However the preferred way is to directly create a new Image instance using the InputStream from the URL.
See also Load image from a file inside a project folder. If done right it does not matter if it is loaded from a jar file or the local file system.

The Image::new(String) constructor is looking for a URL. It is possible to construct a URL for a resource in a jar file, but it's much easier to use ClassLoader::getResource or ClassLoader::getResourceAsStream to manage that for you.
Given the file structure:
src/
SO37054168/
GetResourceTest.java
example/
foo.txt
The following, packaged as a jar will output
package SO37054168;
public class GetResourceTest {
public static void main(String[] args) {
System.out.println(GetResourceTest.class.getClassLoader().getResource("example/foo.txt"));
System.out.println(GetResourceTest.class.getClassLoader().getResourceAsStream("example/foo.txt"));
}
}
jar:file:/home/jeffrey/Test.jar!/example/foo.txt
sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream#7f31245a
Note how the URL for the resource is not the same as the URL you were trying to construct. The protocol is different, and you need to have the ! after the path to the jar file.

Related

JavaFX: ImageView cannot load image that was created by the program itself. Error: java.lang.IllegalArgumentException:

I am attempting to create a program in which the user selects an image from a different folder on their computer and JavaFX copies that image into the project directory for future use. A new folder is created that will store the newly created image file. This is essentially the code for selecting and copying the image into the project directory:
Stage window = (Stage) ap.getScene().getWindow();
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Select Image File");
File selectedFile = fileChooser.showOpenDialog(window);
//Creates a new directory for the new calendar that the user wants to create
File file = new File("src/DefaultUser/" + nameField.getText() + "/");
file.mkdir();
//Creates a new file name with logo as the name, but keeping the extension the same
int index = selectedFile.getName().lastIndexOf(".");
String ext = selectedFile.getName().substring(index);
//Stored in newFileName
String newFileName = "logo" + ext;
File newFile = new File(file.getPath() + "/" + newFileName);
//Copies the selected file into the project src folder, with newFileName as the new file name
Files.copy(selectedFile.toPath(), newFile.toPath());
Then the program moves onto a different scene and thus a different controller actually loads the image into an ImageView. I know that the path for the Image works properly but for whatever reason the program cannot find the image file to load it into the ImageView.
Here is essentially the code used for that:
image.setImage(new Image("DefaultUser/" + imagePath));
Don't worry about what imagePath is in this case because I am absolutely positive it paths to the correct location for the newly created image file. This is because if I close the JavaFX program and rerun it, the image loads properly.
At first, I thought it was because it took time for the image to be copied into the project directory but I checked that the file actually existed within the code and it did so this is apparently not the case. I tried using Thread.sleep() to delay the program a bit so that the code would potentially have more time to copy the file but it still threw the same error: java.lang.IllegalArgumentException: Invalid URL: Invalid URL or resource not found
The strangest part about this is that the program works perfectly fine, it's just that I have to restart the JavaFX program for it to be able to detect the image file, even though I know it exists. Is there something weird about JavaFX and creating new files and accessing them within the same program? I am truly lost. Thank you so much in advance for any help and I'm sorry if this doesn't give enough information because I don't want to have to explain the whole project.
Just like haraldK and James_D said in the comments putting stuff in the src folder is generally a bad idea. I solved the issue by moving the folder out into the project directory instead.

Deploying images used in Java Application with the built jar file

After using images for example on a Button, when I build the application creating the .jar file and execute only the file, the images are not there but would only show if I copy the images folder in the same directory as the jar file. Why is that and how can I resolve this if possible?
I am currently using the following code to set the icon/image:
JButton btn = new JButton("Text", "img/icon.png");
The fact that you can use the images when they are stored outside the jar, suggests that you are doing something of the kind:
File image = new File("directory/image.jpg");
InputStream is = new FileInputStream(image);
This reads a file from a directory on the file system, not from the classpath. Now, if you have packaged your image in a "directory" inside your Jar, you must load the image from the classpath.
InputStream is = getClass().getResourceAsStream("/directory/image.jpg");
(note the slash in the path)
Or
InputStream is = getClass().getClassLoader().getResourceAsStream("directory/image.jpg");
(note the absence of the slash in the path)
Your example, as it is now, should not compile. (The second argument of your JButton construtor is an Icon, not a String, java 8). So when you were getting the image from the file system, you were probably doing something else.
With your example, you need to read an image from the inputstream and convert it to an Icon:
try (InputStream is = getClass.getClassLoader().getResourcesAsStream("directory/image.jpg")) {
BufferedImage image = ImageIO.read(is);
return new JButton("Text", new ImageIcon(image));
} catch (IOException exc) {
throw new RuntimeException(exc);
}
That should use the image that is located in "directory" inside your jar. Of course, you need to include the image within your jar, or you will get a NullPointerException on the inputstream is.
I think you need to understand the
ClassLoader:
A typical strategy is to transform the name into a file name and then
read a "class file" of that name from a file system.
So with this you will be able to lead Resources of your project with getResource
public URL getResource(String name)
Finds the resource with the given name. A resource is some data
(images, audio, text, etc) that can be accessed by class code in a way
that is independent of the location of the code.

Reference resource in top directory (not package) in Java JAR/Eclipse

I'm trying to access some resources located in the top directory of my (Java) project (/resources/imgname.jpg) from a class 'GUI' located in a package 'gui'.
Originally I used the following code:
ImageIcon image = new ImageIcon("./resources/imgname.jpg");
Image img = image.getImage();
This works fine in Eclipse, but doesn't display the image when it's in a runnable JAR. So after some searching it seems you need:
InputStream resource = GUI.class.getResourceAsStream("./resources/imgname.jpg");
try {
Image image = ImageIO.read(resource);
} catch (IOException e) {//trycatch needed because of read method}
Now this doesn't work in either Eclipse or JAR.
I've tried changing reference and location, but the only way I can get the image to display is by placing it in the 'gui' package folder. So is there any way I can reference it in the top folder of the project instead (so I don't have to move the resources to 'gui')?
Thx,
Magic
Try this.
InputStream resource = GUI.class.getClassLoader().getResourceAsStream("resources/imgname.jpg");
It works for me.

Changing the location of files in Java application

I've been putting all of my images for my Java application in a package called "rtype" inside src where I also also have my Class that deals with these images. I wanted to sort the images and put them in a folder of their own. When I do this, The images will no longer load into the class, and I know it's because I changed the file path. I've done some research and tried a few different things. This is basically what I had originally:
String walkingDown = "WalkingDown.gif";
ImageIcon ii;
Image image;
ii = new ImageIcon(this.getClass().getResource(walkingDown));
image = ii.getImage();
and It worked just fine before I moved the location of the images outside the location of the class. Now it cant find the images. Here is what I tried and found online to try (The folders Name is Sprites):
//use getClassLoader() inbetween to find out where exactly the file is
ii = new ImageIcon(this.getClass().getClassLoader().getResource(standingDown));
and
//Changing the path
String walkingDown = "src\\Sprites\\WalkingDown.gif";
//also tried a variation of other paths with no luck
I am using the C drive, but don't want to use "C" in my extension, as I want it to be accessible no matter where I put the project. I am fairly stuck at this point and have done enough looking into it to realize that It was time to ask.
I have a separate "package" for images with that name (in the src folder)
Try something like this:
try {
ClassLoader cl = this.getClass().getClassLoader();
ImageIcon img = new ImageIcon(cl.getResource("images/WalkingDown.gif"));
}
catch(Exception imageOops) {
System.out.println("Could not load program icon.");
System.out.println(imageOops);
}
Your variable is named walkingDown, but you pass in standingDown to the getResource() method.
new ImageIcon("src/Sprites/WalkingDown.gif");

Load Java Image inside package from a class in a different package

I have a Java project called MyProject. I have a few different packages (keeping names simple for the purpose of this question), as follows:
src/PackageA
src/PackageA/PackageAa
src/PackageA/PackageAa/PackageAaa
src/PackageB
src/PackageB/PackageBa
src/PackageB/PackageBa/PackageBaa
I have a class
src/PackageA/PackageAa/PackageAaa/MyJavaFile.java
And I have an image
src/PackageB/PackageBa/PackageBaa/MyImage.png
Inside of MyJavaFile.java, I would like to declare an Image oject of MyImage.png
Image img = new Image(....what goes here?...)
How can I do this?
You could either call Class.getResource and specify a path starting with /, or ClassLoader.getResource and not bother with the /:
URL resource = MyJavaFile.class
.getResource("/PackageB/PackageBa/PackageBaa/MyImage.png");
or:
URL resource = MyJavaFile.class.getClassLoader()
.getResource("PackageB/PackageBa/PackageBaa/MyImage.png");
Basically Class.getResource will allow you to specify a resource relative to the class, but I don't think it allows you to use ".." etc for directory navigation.
Of course, if you know of a class in the right package, you can just use:
URL resource = SomeClassInPackageBaa.class.getResource("MyImage.png");
(I'm assuming you can pass a URL to the Image constructor in question. There's also getResourceAsStream on both Class and ClassLoader.)
you can use relative path since the the relative path is project folder.
ImageIcon img = new ImageIcon("src/PackageB/PackageBa/PackageBaa/MyImage.png");
/folderB/folderBa/folderBaa/MyImage.png
The image can stored into a project folder location .eg: /images/MyImage.png
Then try:
Image img = new Image(/images/MyImage.png);
Using a file path is not possible when running a program that's in a jar file, especially if the program is being loaded as an applet or WebStart application then you can use ClassLoader to get image.
use the following code to load the images:
ClassLoader cldr = this.getClass().getClassLoader();
java.net.URL imageURL = cldr.getResource("/PackageB/PackageBa/PackageBaa/MyImage.png");
ImageIcon aceOfDiamonds = new ImageIcon(imageURL);
This IS the best way to handle all images and icons in a JAR App.
Once you've zipped up all of your images and icons into its own JAR file - Configure your build path by adding the images JAR file into your libraries tab so that its now included in your classpath.
Then simply use the following 3x lines of code at the start of your constuctor to access any image you need for anything including a SystemTray image which doesn't accept the simple ImageIcon's as its main icon (weird I know). The 3x lines are:
URL iconUrl = this.getClass().getResource("/image-iconb.png");
Toolkit tk = this.getToolkit();
imageIcon = tk.getImage(iconUrl);
(imageIcon is just a constructor declared Image variable)
Now you can set a window icon as simply as:
setIconImage(imageIcon );
and at the same time use the same variable when setting the System TrayIcon by declaring:
trayIcon = new TrayIcon(imageIcon, "SystemTray Demo", popupMenu);
The above allows you to declare Images or ImageIcons easily and centrally without running the risk of not keeping image resources in the right place. It keeps it nice and tidy, with the JAR containing all your images automatically compiled at run time and distribution of your program.
As a bonus, once the JAR is registered in your classpath - you can keep adding any other images into the same JAR at any time without any fuss too - Everything just works and the added images are instantly available to your app.
Much better in my view.
Use the getResource method to read resources inside the src root. For example, the following code retrieves images from a folder src/images.
// Get current classloader
ClassLoader cl = this.getClass().getClassLoader();
// Create icons
Icon saveIcon = new ImageIcon(cl.getResource("images/save.gif"));
Icon cutIcon = new ImageIcon(cl.getResource("images/cut.gif"));
The example assumes that the following entries exist in the application's JAR file:
images/save.gif
images/cut.gif
Image img = new Image("./src/PackageB/PackageBa/PackageBaa/MyImage.png");
This shall go the path of the image is first inside src (source) then package so the program would access the image this way.

Categories

Resources