为何将这些显示图像的代码内置到jar中会出现“错误”?

问题描述

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

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

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

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

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

这是我的图像类的代码

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

解决方法

问题出在这里:

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

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

读取资源的正确方法是完全不尝试将其转换为文件。 getResource返回一个URL;您只需将该URL直接传递到the ImageIO.read method that takes a URL

img = ImageIO.read(ImageHQ.class.getResource(path));

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

从InputStream读取

通常来说,有时URL不够用。您也可以使用getResourceAsStream获取从资源读取的开放InputStream。就您而言,您可以这样做:

try (InputStream stream = ImageHQ.class.getResource(path)) {
    img = ImageIO.read(stream);
}

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

资源路径

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

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

ImageHQ.class.getResource("logo.png")

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

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

ImageHQ.class.getResource("/com/example/logo.png")

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

空返回值

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

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

  • 每个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文件浏览器)将其打开。

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

关于打印错误消息

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

关于绘画

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

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...