Why does this code displaying an image give an “”error“” when build into jar?

前端 未结 1 1849
予麋鹿
予麋鹿 2021-01-21 12:30

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

相关标签:
1条回答
  • 2021-01-21 12:54

    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.

    0 讨论(0)
提交回复
热议问题