[英]Why does this code displaying an image give an “”error“” when build into 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, 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 條目只是壓縮存檔的一部分。 它不是硬盤驅動器上的單獨文件。 這就是為什么您不能使用 File 來讀取它。
讀取資源的正確方法是根本不嘗試將其轉換為文件。 getResource
返回一個 URL; 您可以將該 URL 直接傳遞給采用 URL 的 ImageIO.read 方法:
img = ImageIO.read(ImageHQ.class.getResource(path));
請注意使用類文字ImageHQ.class
,而不是 getClass()。 這保證您的資源是相對於您自己的類讀取的,而不是可能位於不同包或不同模塊中的子類。
一般來說,可能存在 URL 不夠用的情況。 您還可以使用getResourceAsStream來獲取從資源中讀取的開放 InputStream。 在你的情況下,你可以這樣做:
try (InputStream stream = ImageHQ.class.getResource(path)) {
img = ImageIO.read(stream);
}
但這不是最佳選擇,因為 URL 可以提供 InputStream 不能提供的信息,例如文件名、內容類型和圖像數據長度的高級知識。
您傳遞給getResource
和getResourceAsStream
的 String 參數實際上不是文件名。 它是相對 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 文件的根目錄。)
如果路徑參數未命名實際存在於 .jar 文件中的資源(或者如果資源位於不允許讀取的模塊中),則所有 getResource* 方法都將返回null
。 NullPointerException 或 IllegalArgumentException 是這種情況的常見症狀。 例如,如果沒有logo.png
與 .jar 文件中的 ImageHQ 類在同一個包中,getResource 將返回 null,並將該 null 傳遞給ImageIO.read
將導致 IllegalArgumentException,如 ImageIO.read 文檔中所述.
如果發生這種情況,您可以通過列出其內容來對 .jar 文件進行故障排除。 有多種方法可以做到這一點:
jar tf /path/to/myapplication.jar
。unzip -v /path/to/myapplication.jar
也可以使用,因為 .jar 文件實際上是一個帶有一些 Java 特定條目的 zip 文件。 回到示例,如果您的類在com.example
包中並且您的代碼正在執行ImageHQ.class.getResource("logo.png")
,您將檢查 .jar 文件的內容以獲取com/example/logo.png
條目。 如果它不存在,getResource 方法將返回 null。
用ex.getMessage()
替換ex.getMessage()
ex.toString()
。 通常情況下,異常的消息本身是沒有意義的。 您還應該添加ex.printStackTrace();
到每個catch
塊(或添加一個記錄堆棧跟蹤的日志語句),這樣您就可以准確地知道問題發生在哪里。
永遠不要從paintComponent 方法調用repaint()
。 這將創建一個無限循環,因為repaint()
將強制 Swing 繪畫系統再次調用paintComponent
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.