为什么这段显示图像的代码在构建到 jar 中时会出现“错误”?

2023-12-10

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

x/y Offset是在JLabel的中间绘制一个较小的图像。

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

如果我现在将类构建到 jar 文件中,它就不再起作用了。

我尝试将图像设置为 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, 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方法也没有给我一个错误。


问题就出在这里:

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

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

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

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

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

从输入流读取

一般来说,在某些情况下 URL 可能不够。您还可以使用获取资源作为流获取从资源读取的打开的InputStream。对于你的情况,你可以这样做:

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

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

资源路径

您传递给的 String 参数getResource and getResourceAsStream实际上不是文件名。它是相对 URL 的路径部分。这意味着一个论证开始于,比如说,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 文件的根目录。)

返回值为空

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

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

  • 每个 IDE 的文件浏览器或文件树都可以检查 .jar 文件的内容。
  • 如果您的 JDK 在 shell 的路径中,您只需执行以下操作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。

关于打印错误消息

Replace ex.getMessage() with ex.toString()。通常情况下,异常消息本身是没有意义的。您还应该添加ex.printStackTrace();每一个catch块(或添加记录堆栈跟踪的日志语句),这样您就可以准确地知道问题发生的位置。

关于绘画

Never call repaint()来自paintComponent 方法。这会创建一个无限循环,因为repaint()将强制 Swing 绘画系统调用paintComponent again.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么这段显示图像的代码在构建到 jar 中时会出现“错误”? 的相关文章

随机推荐