我正在阅读一些教程,我在将字体文件加载到Eclipse Java Project时遇到了问题。我在SO上尝试了很多解决方案,最终找到了一个(使用FileInputStream),它对我有用,但是当项目导出为runnable JAR时却没有。另一方面,在我加载图标的其他项目中使用相同的目录结构,所以我猜问题不在路径本身。
这是目录结构:
以下是代码:
package examples;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URISyntaxException;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class Test01 extends JPanel {
String text = "Free the bound periodicals";
Font fon;
FileInputStream fis;
// InputStream fis;
@Override
public void paintComponent(Graphics comp) {
Graphics2D comp2D = (Graphics2D) comp;
// This (for another project) works both in Eclipse and in runnable JAR
// ImageIcon loadIcon = new ImageIcon(getClass().getResource("/examples/resources/load.gif"));
// This (quite contrary to many SO suggestions) doesn't work in Eclipse for this project, why?
// fis = this.getClass().getResourceAsStream("/examples/resources/vedrana.ttf");
// This (quite contrary to many SO suggestions) doesn't work in Eclipse for this project, why?
// fis = this.getClass().getClassLoader().getResourceAsStream("/examples/resources/verdana.ttf");
// This works within Eclipse project but not when exported to runnable JAR,
// Moreover many suggest that InputStream should be favored over FileInputStream
try {
fis = new FileInputStream(new File(getClass().getResource("/examples/resources/verdana.ttf").toURI()));
} catch (FileNotFoundException e1) {
JOptionPane.showMessageDialog(this, "FileNotFoundException!");
} catch (URISyntaxException e1) {
JOptionPane.showMessageDialog(this, "URISyntaxException!");
} catch (Exception e1) {
JOptionPane.showMessageDialog(this, "NullPointerException!");
}
try {
fon = Font.createFont(Font.TRUETYPE_FONT, fis);
} catch (FontFormatException e) {
// TODO Auto-generated catch block
System.out.println("Error - FontFormatException " + e.getMessage());
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("Error - IOException " + e.getMessage());
}
fon = fon.deriveFont(Font.PLAIN, 72);
FontMetrics metrics = getFontMetrics(fon);
comp2D.setFont(fon);
comp2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int x = (getSize().width - metrics.stringWidth(text)) / 2;
int y = getSize().height / 2;
comp2D.drawString(text, x, y);
}
public static void main(String[] args) {
JFrame mainFrame = new JFrame("Main Menu");
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setSize(1000, 250);
mainFrame.setVisible(true);
mainFrame.add(new Test01());
// mainFrame.pack();
}
}
所以,困扰我的是:
- 为什么这不起作用(似乎无法找到字体文件),因为它抛出 的NullPointerException
fis = this.getClass().getResourceAsStream("/examples/resources/vedrana.ttf");
这也没有用
fis = this.getClass().getClassLoader().getResourceAsStream("/examples/resources/verdana.ttf");
- 为什么这在Eclipse项目中有效,但在导出时无效 可运行的JAR
fis = new FileInputStream(new File(getClass().getResource("/examples/resources/verdana.ttf").toURI()));
更新 事实证明这将有效:
fis = this.getClass().getClassLoader().getResourceAsStream("examples/resources/verdana.ttf");
问题出在第一个斜杠 - 路径应该是“examples / resources / verdana.ttf”而不是“/examples/resources/verdana.ttf”。它适用于Eclipse和可运行的JAR。
现在,让我感到兴趣的是,为什么在这种情况下需要第一次斜线
ImageIcon loadIcon = new ImageIcon(getClass().getResource("/examples/resources/load.gif"));
更新2 : 在感到沮丧之后为什么这种方法不起作用
InputStream fis = this.getClass().getResourceAsStream("/examples/resources/verdana.ttf");
我从Eclipse中删除了整个类,现在BOTH方法在Eclipse和runanble JAR中工作 - 就像它应该的那样。
InputStream fis = this.getClass().getResourceAsStream("/examples/resources/verdana.ttf");
或
InputStream fis = this.getClass().getClassLoader().getResourceAsStream("examples/resources/verdana.ttf");
答案 0 :(得分:3)
回答您关于斜杠的最后一个问题:当您使用getClass()
时,搜索将从调用类开始。如果您不使用第一个类,则将在examples/examples/resource
中查找该文件(因为调用类位于examples
中),该文件不存在。正斜杠的作用是将搜索带到类路径根目录。
这在Eclipse项目中有效,但在导出到可运行的JAR时没有, 此外,许多人建议
InputStream
应优先于FileInputStream
如果您的应用程序需要一个资源并且打包在jar中,那么应从URL中读取,通过getClass().getResource()
返回资源的实际URL,或getClass().getResourceAsStream()
以InputStream
的形式将资源作为流返回,从URL获取。您也可以getClassLoader()
,但这里有主要区别
getClass()
- 如上所述,搜索将从调用类的位置开始。所以无论这个类在哪个包中,搜索就开始了。像这样的结构
ProjectRoot
src
com
example
MyClass.class
resources
resource.ttf
将导致搜索从包的example
目录内开始。因此,如果您尝试使用此路径resources/resource.ttf
,则会失败,因为resources
目录中的 没有examples
目录。使用/
会将搜索带到类路径的根目录,即src
(至少从IDE的角度来看 - 这会将jar变为classes
)。因此/resources/resource.ttf
可以正常工作,因为src/resources/resource.ttf
存在。
getClassLoader()
- 从类路径根开始搜索,src
。您不需要额外的/
,因为resources/resource.ttf
就像是将其存在为src/resources/resource.ttf
。
另一方面,当您使用任何形式的File
搜索时,搜索将根据本地文件系统进行。关于为什么使用File
可以在IDE上运行的问题,是因为IDE不是从jar启动程序,而是从IDE本身启动,并使用当前工作目录作为根路径。
因此,要记住的主要事情是,当您的文件是应用程序资源时,请从类路径中读取它。
InputStream is = getClass().getResourcAsStream("/path/to/file");
有关IDE功能的进一步说明。你的是日食。如果您在yout文件系统中找到项目,您将看到一个bin
文件,就IDE而言,它是类路径。编译代码时,资源会被复制到该路径中。这是IDE在您尝试读取文件时搜索的位置,因为项目是工作目录,并且使用非绝对路径,这就是搜索发生的位置
答案 1 :(得分:1)
虽然@peeskillet提供了非常简洁的答案,如果有人想要做额外的阅读,也可以在这里找到非常好的解释:
我提到这个,因为它似乎有很多问题和关于使用不同方法在Java中加载文件的正确方法的混淆(包括我自己)。
所以最后两种方法都有效:
// This works both within Eclipse project and in runnable JAR
InputStream fis = this.getClass().getResourceAsStream("/examples/resources/verdana.ttf");
或
// This works both within Eclipse project and in runnable JAR
InputStream fis = this.getClass().getClassLoader().getResourceAsStream("examples/resources/verdana.ttf");