为什么这段显示图像的代码在构建到 jar 中时会给出“"error"”?
Why does this code displaying an image give an ""error"" when build into jar?
我想通过在 JLabel 上绘制 BufferedImage 来显示图像。
x/yOffset是在JLabel的中间画一个小图
如果我 运行 我 IDE 中的代码它工作正常并在我的 JFrame 上显示图像。
如果我现在将 Class 构建到 jar 文件中,它将不再起作用。
我在不使用 BufferedImage 的情况下尝试将图像设置为 JLabel 的图标,但这不是我想要做的。
这是我的图片代码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方法也不给我报错
问题出在这里:
new File(getClass().getResource(path).toURI())
应用程序资源不保证是一个单独的文件。 .jar 条目只是压缩存档的一部分。它不是硬盘上的单独文件。这就是为什么您不能使用文件来读取它的原因。
读取资源的正确方法是根本不要尝试将其转换为文件。 getResource
return一个URL;您可以将 URL 直接传递给 the ImageIO.read method that takes a URL:
img = ImageIO.read(ImageHQ.class.getResource(path));
请注意使用 class 文字 ImageHQ.class
,而不是 getClass()。这保证您的资源是相对于您自己的 class 读取的,而不是可能位于不同包或不同 module.
中的子 class
从 InputStream 读取
一般来说,可能存在 URL 不够用的情况。您还可以使用 getResourceAsStream 获取从资源读取的打开的 InputStream。在你的情况下,你可以这样做:
try (InputStream stream = ImageHQ.class.getResource(path)) {
img = ImageIO.read(stream);
}
但这将是 sub-optimal,因为 URL 可以提供 InputStream 无法提供的信息,例如文件名、内容类型和图像数据长度的预知信息。
资源路径
您传递给 getResource
和 getResourceAsStream
的字符串参数实际上不是文件名。它是相对 URL 的路径部分。这意味着以 C:\
开头的参数将始终失败。
因为参数是 URL,所以它在所有平台上总是使用正斜杠 (/
) 来分隔路径组件。通常是针对调用getResource*方法的class对象的包进行解析;所以,如果 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 不允许在其字符串参数的开头使用 /
,并且始终假定该参数相对于 .jar 文件的根。)
空 return 值
所有 getResource* 方法将 return null
如果路径参数没有命名实际存在于 .jar 文件中的资源(或者如果资源在不存在的模块中允许阅读)。 NullPointerException 或 IllegalArgumentException 是这种情况的常见症状。例如,如果没有 logo.png
与 .jar 文件中的 ImageHQ class 在同一个包中,则 getResource 将为 return 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-specific 条目的 zip 文件。
- 在 Windows 中,您可以复制 .jar 文件,将副本的扩展名更改为 .zip,然后使用任何压缩工具打开它,包括 Windows 文件资源管理器。
回到示例,如果您的 class 在 com.example
包中并且您的代码正在执行 ImageHQ.class.getResource("logo.png")
,您将检查 .jar 文件的内容以获取com/example/logo.png
条目。如果不存在,getResource 方法将 return null。
关于打印错误信息
将 ex.getMessage()
替换为 ex.toString()
。通常情况下,异常的消息本身是没有意义的。您还应该将 ex.printStackTrace();
添加到每个 catch
块(或添加记录堆栈跟踪的日志记录语句),以便您确切知道问题发生的位置。
关于绘画
从不 从 paintComponent 方法调用 repaint()
。这将创建一个无限循环,因为 repaint()
将强制 Swing 绘画系统再次调用 paintComponent
。
我想通过在 JLabel 上绘制 BufferedImage 来显示图像。
x/yOffset是在JLabel的中间画一个小图
如果我 运行 我 IDE 中的代码它工作正常并在我的 JFrame 上显示图像。
如果我现在将 Class 构建到 jar 文件中,它将不再起作用。
我在不使用 BufferedImage 的情况下尝试将图像设置为 JLabel 的图标,但这不是我想要做的。
这是我的图片代码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方法也不给我报错
问题出在这里:
new File(getClass().getResource(path).toURI())
应用程序资源不保证是一个单独的文件。 .jar 条目只是压缩存档的一部分。它不是硬盘上的单独文件。这就是为什么您不能使用文件来读取它的原因。
读取资源的正确方法是根本不要尝试将其转换为文件。 getResource
return一个URL;您可以将 URL 直接传递给 the ImageIO.read method that takes a URL:
img = ImageIO.read(ImageHQ.class.getResource(path));
请注意使用 class 文字 ImageHQ.class
,而不是 getClass()。这保证您的资源是相对于您自己的 class 读取的,而不是可能位于不同包或不同 module.
从 InputStream 读取
一般来说,可能存在 URL 不够用的情况。您还可以使用 getResourceAsStream 获取从资源读取的打开的 InputStream。在你的情况下,你可以这样做:
try (InputStream stream = ImageHQ.class.getResource(path)) {
img = ImageIO.read(stream);
}
但这将是 sub-optimal,因为 URL 可以提供 InputStream 无法提供的信息,例如文件名、内容类型和图像数据长度的预知信息。
资源路径
您传递给 getResource
和 getResourceAsStream
的字符串参数实际上不是文件名。它是相对 URL 的路径部分。这意味着以 C:\
开头的参数将始终失败。
因为参数是 URL,所以它在所有平台上总是使用正斜杠 (/
) 来分隔路径组件。通常是针对调用getResource*方法的class对象的包进行解析;所以,如果 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 不允许在其字符串参数的开头使用 /
,并且始终假定该参数相对于 .jar 文件的根。)
空 return 值
所有 getResource* 方法将 return null
如果路径参数没有命名实际存在于 .jar 文件中的资源(或者如果资源在不存在的模块中允许阅读)。 NullPointerException 或 IllegalArgumentException 是这种情况的常见症状。例如,如果没有 logo.png
与 .jar 文件中的 ImageHQ class 在同一个包中,则 getResource 将为 return 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-specific 条目的 zip 文件。 - 在 Windows 中,您可以复制 .jar 文件,将副本的扩展名更改为 .zip,然后使用任何压缩工具打开它,包括 Windows 文件资源管理器。
回到示例,如果您的 class 在 com.example
包中并且您的代码正在执行 ImageHQ.class.getResource("logo.png")
,您将检查 .jar 文件的内容以获取com/example/logo.png
条目。如果不存在,getResource 方法将 return null。
关于打印错误信息
将 ex.getMessage()
替换为 ex.toString()
。通常情况下,异常的消息本身是没有意义的。您还应该将 ex.printStackTrace();
添加到每个 catch
块(或添加记录堆栈跟踪的日志记录语句),以便您确切知道问题发生的位置。
关于绘画
从不 从 paintComponent 方法调用 repaint()
。这将创建一个无限循环,因为 repaint()
将强制 Swing 绘画系统再次调用 paintComponent
。