为什么此显示图像的代码会在屏幕上显示“错误"字样?什么时候装进罐子里? [英] Why does this code displaying an image give an ""error"" when build into jar?

查看:107
本文介绍了为什么此显示图像的代码会在屏幕上显示“错误"字样?什么时候装进罐子里?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想通过在JLabel上绘制一个BufferedImage来显示图像.

I want to display an Image in a JLabel by drawing a BufferedImage on it.

x/yOffset用于在JLabel的中间位置绘制较小的图像.

The x/yOffset is to draw a smaller image in the middel of the JLabel.

如果我在IDE中运行代码,它可以正常工作并在JFrame上显示图像.

If I run the code in my IDE it works fine and displays the image on my JFrame.

如果我现在将类构建到jar文件中,它将无法正常工作.

If I now build the Class into a jar file it does not work anymore.

我尝试将Image设置为JLabel的图标,而不使用BufferedImage,但这不是我想要做的.

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:errorMsg方法也不会给我一个错误.

PS: The errorMsg method also does not give me an error.

推荐答案

问题出在这里:

new File(getClass().getResource(path).toURI())

不能 将应用程序资源保证为单独的文件. .jar条目只是压缩存档的一部分.它不是硬盘驱动器上的单独文件.这就是为什么您不能使用File来读取它的原因.

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.

读取资源的正确方法是完全不尝试将其转换为文件. getResource返回一个URL;您可以将该URL直接传递给采用URL的ImageIO.read方法:

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));

请注意使用类文字ImageHQ.class代替getClass().这保证了您的资源是相对于您自己的类而言的,而不是相对于可能位于不同包中或不同的

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.

通常来说,有时URL不够用.您还可以使用

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);
}

但这不是次优的,因为URL可以提供InputStream无法提供的信息,例如文件名,内容类型和图像数据长度的高级知识.

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.

您传递给getResourcegetResourceAsStream的String参数实际上不是文件名.这是相对网址的路径部分.这意味着以C:\开头的参数将始终失败.

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.

由于参数是URL,因此在所有平台上,它始终使用正斜杠(/)分隔路径组件.通常,它是通过调用getResource *方法的类对象的包来解析的;因此,如果ImageHQ位于com.example软件包中,则此代码:

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")

将在.jar文件中查找com/example/logo.png.

would look for com/example/logo.png in the .jar file.

您可以选择以反斜杠开头的String参数,这将强制其相对于.jar文件的根目录.以上内容可以写为:

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")

ClassLoader中也有getResource *方法,但不应使用这些方法.始终改用Class.getResource或Class.getResourceAsStream. ClassLoader方法在Java 8和更早的版本中在功能上相似,但是从Java 9开始,Class.getResource在模块化程序中更安全,因为它不会与模块封装冲突. (ClassLoader.getResource在其String参数的开头不允许/,并且始终假定该参数相对于.jar文件的根.)

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.)

如果path参数没有命名.jar文件中实际存在的资源(或者如果该资源位于不允许读取的模块中),则所有getResource *方法都将返回null. NullPointerException或IllegalArgumentException是这种情况的常见症状.例如,如果没有logo.png与.jar文件中的ImageHQ类位于同一包中,则getResource将返回null,并将该null传递给ImageIO.read将导致IllegalArgumentException,如ImageIO.read文档中所述.

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.

如果发生这种情况,您可以通过列出.jar文件的内容来对其进行故障排除.有很多方法可以做到这一点:

If this occurs, you can troubleshoot the .jar file by listing its contents. There are a number of ways to do this:

  • 每个IDE的文件浏览器或文件树都可以检查.jar文件的内容.
  • 如果您的JDK位于外壳程序的路径中,则只需执行jar tf /path/to/myapplication.jar.
  • 在Unix和Linux中,unzip -v /path/to/myapplication.jar也将起作用,因为.jar文件实际上是具有一些Java特定条目的zip文件.
  • 在Windows中,您可以复制.jar文件,将副本的扩展名更改为.zip,然后使用任何zip工具(包括Windows文件浏览器)将其打开.
  • 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.

回到该示例,如果您的类在com.example包中,并且您的代码在执行ImageHQ.class.getResource("logo.png"),则应在.jar文件的内容中查找com/example/logo.png条目.如果不存在,则getResource方法将返回null.

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.

ex.getMessage()替换为ex.toString().通常情况下,异常消息本身是毫无意义的.您还应该在每个catch块中添加ex.printStackTrace();(或添加一个记录堆栈跟踪的日志记录语句),这样您才能确切知道问题出在哪里.

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.

从不从paintComponent方法调用repaint().这会创建一个无限循环,因为repaint()将强制Swing绘画系统再次调用paintComponent.

Never call repaint() from a paintComponent method. This creates an infinite loop, because repaint() will force the Swing painting system to call paintComponent again.

这篇关于为什么此显示图像的代码会在屏幕上显示“错误"字样?什么时候装进罐子里?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆