Jump to content


Retrieving A Picture File From Within A Jar File


  • Please log in to reply
4 replies to this topic

#1 ham90mack

ham90mack

    Advanced Member

  • Members
  • PipPipPip
  • 937 posts

Posted 21 June 2007 - 11:58 PM

This is explained on many web sites, but after writing the tutorial on how to make a JAR file with ANT, I assumed that this question may come up sooner or later.

Here is a class that retrieves a picture file outside a jar file, from a URL, or inside a jar file.
package images;

import java.awt.Image;
import java.awt.MediaTracker;
import java.net.URL;
import javax.swing.ImageIcon;

/**
This is a utility class that allows images to be loaded from file, from a URL, or
from a JAR file.<p>
COPYRIGHT  2007 ham90mack. All Rights Reserved.

@author ham90mack
@version 1.20 2007-06-22
*/
public class ImageRetriever
{
/**
This class is a utility and should never be constructed.
*/
private ImageRetriever(){}

/**
Retrieves an ImageIcon from file. This method first tries to get the image from
the base folder that is defines when starting the JVM. If the image is not found,
then this attempts to get the image from a folder named "images". If the image is
not found, then this attempts to load the image from a URL. If the image is not
found or if the String is not a valid URL, then this attempts to load the image from
the JAR file this may be in. Note that if an image is expected to be in a folder within
the jar file named images, "/images/" does not need to be provided. Otherwise, make sure
to put "/" at the beginning of the filename. If the image is not found, a RuntimeException
is thrown.
@param filename the image file to retrieve
@return the ImageIcon created from the file
@throws RuntimeException if the icon does not exist
@see #getImage(String)
*/
public static ImageIcon getImageIcon(String filename)
{
// to read from file
ImageIcon icon = new ImageIcon(filename);

//checks if the icon is in a folder named images
if ((icon == null) || (icon.getImageLoadStatus() != MediaTracker.COMPLETE)){
icon = new ImageIcon("images/" + filename);
}

// try to read from URL
if ((icon == null) || (icon.getImageLoadStatus() != MediaTracker.COMPLETE)) {
try {
URL url = new URL(filename);
icon = new ImageIcon(url);
} catch (Exception e) { /* not a url */ }
}

// in case file is inside a .jar
if ((icon == null) || (icon.getImageLoadStatus() != MediaTracker.COMPLETE)) {
URL url = ImageRetriever.class.getResource(filename);
if (url == null) throw new RuntimeException("image " + filename + " not found");
icon = new ImageIcon(url);
}

return icon;
}

/**
Retrieves an Image from a file. Calls getImageIcon to get the image and returns the
Image within the ImageIcon.
@param filename the image file to retrieve
@return the Image created from the file
@throws RuntimeException if the icon does not exist
@see #getImageIcon(String)
*/
public static Image getImage(String filename)
{
ImageIcon icon = getImageIcon(filename);

return icon.getImage();
}
}
The bulk of what is happening is within the getImageIcon method. First it tries to get the icon from a file. If it doesn't exist, it tries to get the icon from a folder named images. If it doesn't exist, it tries to get the icon from a URL. If the String is not a valid URL or if it doesn't exist, it tries to get it from inside a JAR file using the Class getResource method. If it is still not found, a RuntimeException is thrown. If an ImageIcon is needed in a class that will be in a JAR file, this is how to use this:
CODE
images.ImageRetriever.getImageIcon("blah.png");

Or import images and just use ImageRetriever.getImageIcon("blah.png");

NOTE: Please read the comment for getImageIcon for further clarification.

Edited by ham90mack, 22 June 2007 - 02:35 PM.

ham90mack
http://ham90mack.googlepages.com
Resistance may be futile,
But capacitance has potential.
BLAH!

#2 Captain Pierce

Captain Pierce

    Advanced Member

  • Moderator
  • PipPipPip
  • 877 posts
  • Gender:Male
  • Location:Georgia

Posted 22 June 2007 - 12:15 PM

1 suggestion for improvement:

If you feel like specifying an "image" folder is absolutely necessary, it really shouldn't be hard-coded into the getImageIcon() method. How about storing it in a String object, that way another programmer can choose his/her own name for their image folder.



#3 ham90mack

ham90mack

    Advanced Member

  • Members
  • PipPipPip
  • 937 posts

Posted 22 June 2007 - 12:23 PM

Sorry, can't do that. You see, with Class.getResource(), if it is in a JAR file it starts where the class file actually is. Since this class is in a package named images, the getResource only works within the scope of the package folder images within the jar file. Outside of a jar file is fine, but inside is a different story. Of course, if I did not put this class within a package it could see everything in a jar file. However, any projects that have a package structure cannot see classes without a package. So instead of putting it in a package like ham90mack.images or no package at all, I put it in the more obvious package of images. I decided that it would be easier to hard code the folder images for outside of a jar file because this class was made to be used in a jar file, but it can be tested outside of one if necessary. It is easier to group your different kinds of resources in folders anyway, isn't it?
ham90mack
http://ham90mack.googlepages.com
Resistance may be futile,
But capacitance has potential.
BLAH!

#4 Captain Pierce

Captain Pierce

    Advanced Member

  • Moderator
  • PipPipPip
  • 877 posts
  • Gender:Male
  • Location:Georgia

Posted 22 June 2007 - 02:18 PM

Umm, actually if you look at the documentation, that's not exactly how getResource works. Let me make sure I understand your explanation of it though. You claim it only works with a file path like:

jar file:
+--package
----+---class
----+---images

The images folder must be in the same directory in the jar as the class that wants access to that resource...

And as your code is currently written, yeah I suppose that's right BUT there is an incredibly simple work-around that is actually described in the getResource API documentation and it involves using the '/' character.

Here's an example from a project of mine:

CODE
public JBinaryFrame() {
        initComponents();
        //setIconImage(new ImageIcon(this.getClass().getResource("/Resources/JBinaryIcon.png")).getImage());
        fileButton.setIcon(new ImageIcon(JBinaryFrame.class.getResource("/Resources/open.png")));
        destinationChooser.setAcceptAllFileFilterUsed(false);
        destinationChooser.setFileFilter(binaryFilter);
    }


*Note: The commented line was actually generated by Netbeans. I re-wrote to mimmick your getResource call exactly. Just in case...I just copied/pasted it from initComponents to there for reference as I re-wrote and left it as another example.

My JAR file is broken down as follows:

JBinary.jar
+---JBinary
+---Resources

Classes in one folder, image resources in another.

The first '/' makes all the difference, it changes how the absolute path of the resource is calculated. Try deleting it. Java fails to find the file, just as you said it would.

#5 ham90mack

ham90mack

    Advanced Member

  • Members
  • PipPipPip
  • 937 posts

Posted 22 June 2007 - 02:22 PM

Oh...  I guess that is sort of like using new File("C:\blah") verses new File("blah").  Ok, I'm going to change my code...
ham90mack
http://ham90mack.googlepages.com
Resistance may be futile,
But capacitance has potential.
BLAH!