加载字体文件时,Eclipse Java File FileInputStream vs Input Stream

时间:2014-05-25 10:03:08

标签: java eclipse jar inputstream fileinputstream

我正在阅读一些教程,我在将字体文件加载到Eclipse Java Project时遇到了问题。我在SO上尝试了很多解决方案,最终找到了一个(使用FileInputStream),它对我有用,但是当项目导出为runnable JAR时却没有。另一方面,在我加载图标的其他项目中使用相同的目录结构,所以我猜问题不在路径本身。

这是目录结构enter image description here

以下是代码:

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");

2 个答案:

答案 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提供了非常简洁的答案,如果有人想要做额外的阅读,也可以在这里找到非常好的解释:

Loading Java Files

我提到这个,因为它似乎有很多问题和关于使用不同方法在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");