问题
I want to display an Image in a JLabel by drawing a BufferedImage on it.
The x/yOffset is to draw a smaller image in the middel of the JLabel.
If I run the code in my IDE it works fine and displays the image on my JFrame.
If I now build the Class into a jar file it does not work anymore.
I tried it with setting the Image as an icon for the JLabel without using BufferedImage but thats not what I want to do.
Here is the Code of my Image Class:
public class ImageHQ extends JLabel {
BufferedImage img;
int xOffset=0;
int yOffset=0;
public ImageHQ(String path, int xOffset, int yOffset) {
try {
try {
img = ImageIO.read(new File(getClass().getResource(path).toURI()));
} catch (URISyntaxException ex) {
Logger.getLogger(ImageHQ.class.getName()).log(Level.SEVERE, null, ex);
errorMsg(ex.getMessage());
}
} catch (IOException ex) {
Logger.getLogger(ImageHQ.class.getName()).log(Level.SEVERE, null, ex);
errorMsg(ex.getMessage());
}
this.xOffset = xOffset;
this.yOffset = yOffset;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.drawImage(img, 0+xOffset, 0+yOffset, null);
repaint();
}
public void errorMsg(String msg) {
JOptionPane.showMessageDialog(null, msg, "Fehler", JOptionPane.ERROR_MESSAGE);
}
}
PS: The errorMsg method also does not give me an error.
回答1:
The problem lies here:
new File(getClass().getResource(path).toURI())
An application resource is not guaranteed to be a separate file. A .jar entry is just part of a compressed archive. It’s not a separate file on the hard drive. That is why you cannot use File to read it.
The correct way to read a resource is by not attempting to convert it to a file at all. getResource
returns a URL; you can just pass that URL directly to the ImageIO.read method that takes a URL:
img = ImageIO.read(ImageHQ.class.getResource(path));
Note the use of a class literal, ImageHQ.class
, instead of getClass(). This guarantees that your resource is read relative to your own class, and not a subclass which might be in a different package or different module.
Reading from an InputStream
Generally speaking, there may be cases where a URL isn’t sufficient. You can also use getResourceAsStream to obtain an open InputStream that reads from the resource. In your case, you could do:
try (InputStream stream = ImageHQ.class.getResource(path)) {
img = ImageIO.read(stream);
}
But this would be sub-optimal, since a URL can provide information that an InputStream cannot, like a file name, content type, and advance knowledge of the image data length.
Resource paths
The String argument you pass to getResource
and getResourceAsStream
is not actually a file name. It’s the path portion of a relative URL. This means an argument that starts with, say, C:\
will always fail.
Because the argument is a URL, it always uses forward slashes (/
) to separate path components, on all platforms. Normally, it is resolved against the package of the class object whose getResource* method is called; so, if ImageHQ
were in the com.example
package, this code:
ImageHQ.class.getResource("logo.png")
would look for com/example/logo.png in the .jar file.
You can optionally start the String argument with a slash, which will force it to be relative to the root of the .jar file. The above could be written as:
ImageHQ.class.getResource("/com/example/logo.png")
There are also getResource* methods in ClassLoader, but those should not be used. Always use Class.getResource or Class.getResourceAsStream instead. The ClassLoader methods are functionally similar in Java 8 and earlier, but as of Java 9, Class.getResource is safer in modular programs because it won’t run afoul of module encapsulation. (ClassLoader.getResource does not allow /
at the start of its String argument, and always assumes the argument is relative to the root of a .jar file.)
null return values
All getResource* methods will return null
if the path argument does not name a resource that actually exists in the .jar file (or if the resource is in a module that doesn’t allow it to be read). A NullPointerException or IllegalArgumentException is a common symptom of this. For instance, if no logo.png
was in the same package as the ImageHQ class in the .jar file, getResource would return null, and passing that null to ImageIO.read
would result in an IllegalArgumentException, as stated in the ImageIO.read documentation.
If this occurs, you can troubleshoot the .jar file by listing its contents. There are a number of ways to do this:
- Every IDE’s file explorer or file tree can examine the contents of a .jar file.
- If your JDK is in your shell’s Path, you can simply do
jar tf /path/to/myapplication.jar
. - In Unix and Linux,
unzip -v /path/to/myapplication.jar
will also work, since a .jar file is actually a zip file with a few Java-specific entries. - In Windows, you can make a copy of the .jar file, change the copy’s extension to .zip, and open it with any zip tool, including the Windows file explorer.
Going back to the example, if your class were in the com.example
package and your code were doing ImageHQ.class.getResource("logo.png")
, you would check the contents of the .jar file for a com/example/logo.png
entry. If it’s not there, the getResource method will return null.
Regarding printing the error message
Replace ex.getMessage()
with ex.toString()
. It is often the case that an exception’s message is meaningless by itself. You should also add ex.printStackTrace();
to each catch
block (or add a logging statement that records the stack trace), so you’ll know exactly where the problem occurred.
Regarding painting
Never call repaint()
from a paintComponent method. This creates an infinite loop, because repaint()
will force the Swing painting system to call paintComponent
again.
来源:https://stackoverflow.com/questions/63890530/why-does-this-code-displaying-an-image-give-an-error-when-build-into-jar