加载字体文件时的Eclipse Java文件FileInputStream与输入流[重复]

Posted

技术标签:

【中文标题】加载字体文件时的Eclipse Java文件FileInputStream与输入流[重复]【英文标题】:Eclipse Java File FileInputStream vs Input Stream when loading font file [duplicate] 【发布时间】:2014-07-14 06:47:51 【问题描述】:

我正在阅读一些教程,但在将字体文件加载到 Eclipse Java 项目时遇到问题。我在 SO 上尝试了许多建议的解决方案,最终找到了一个(使用 FileInputStream)对我有用,但在项目导出为可运行 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 中删除了整个类,现在这两种方法都可以在 Eclipse 和 runanble JAR 中工作 - 就像它应该的那样。

InputStream fis = this.getClass().getResourceAsStream("/examples/resources/verdana.ttf");

InputStream fis = this.getClass().getClassLoader().getResourceAsStream("examples/resources/verdana.ttf");

【问题讨论】:

@Darkhogg 您指向的帖子不是对我帖子的完整回答,只是(可能)对 JAR 部分的回答。如果您阅读我的帖子,您会发现我在实施他们建议的解决方案时遇到问题 - 即使用 getResourceAsStream(),这就是我问这个问题的原因。 【参考方案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");

【讨论】:

【参考方案2】:

回答你关于斜线的最后一个问题:当你使用getClass() 时,搜索将从调用类开始。如果您不使用第一个类,该文件将在不存在的examples/examples/resource 中查找(因为调用类在examples 中)。正斜杠的作用是将搜索带到类路径根目录。

这在 Eclipse 项目中有效,但在导出到可运行 JAR 时无效, 此外,许多人建议InputStream 应优先于FileInputStream

如果您的应用程序需要某个资源并且将其打包在 jar 中,则应该从 URL 中读取,通过 getClass().getResource() 返回资源的实际 URL,或 @ 987654327@ 以InputStream 的形式将资源作为流返回,从URL 中获取。你也可以getClassLoader(),但这里是主要区别

getClass() - 如上所述,搜索将从调用类的位置开始。所以无论类在什么包中,这就是搜索开始的地方。像这样的结构

ProjectRoot
          src
             com
                example
                     MyClass.class
             resources
                    resource.ttf

将导致搜索从包的example 目录内部开始。因此,如果您尝试使用此路径 resources/resource.ttf if 将失败,因为examples 目录中没有resources 目录。使用/ 会将搜索带到类路径的根目录,即src(至少从IDE 的角度来看——它将被压缩到classes)。所以/resources/resource.ttf 可以工作,因为src/resources/resource.ttf 存在。

getClassLoader() - 从类路径根目录开始搜索,所以是src。你不需要额外的/,因为resources/resource.ttf就像说它作为src/resources/resource.ttf存在。


另一方面,当您使用任何形式的File 搜索时,搜索将根据本地文件系统进行。至于您的问题,为什么在您的 IDE 上使用 File 是因为 IDE 不是从 jar 启动程序,而是从 IDE 本身启动程序,并使用当前工作目录作为根路径。

所以要记住的主要事情是,当您的文件是应用程序资源时,请从类路径中读取它。

InputStream is = getClass().getResourcAsStream("/path/to/file");

关于 IDE 功能的进一步说明。你的是日食。如果你在你的文件系统中去你的项目,你会看到一个bin 文件,就你的IDE而言是类路径。编译代码时,资源会被复制到该路径中。这就是当您尝试读取文件时 IDE 将搜索的位置,因为项目是工作目录,并且使用非绝对路径,这就是搜索发生的位置

【讨论】:

谢谢你,我给了+1,如果没有人提供更简洁的答案,我会接受你的答案作为最终答案。但我怀疑有人会匹配这个答案。 只要你从我的解释中完全理解,这就是最重要的:-) 好的,这似乎是这种情况下的通用解决方案,因为它似乎在 IDE 和可运行的 JAR 中都可以工作,你同意吗? InputStream fis = this.getClass().getClassLoader().getResourceAsStream("examples/resources/verdana.ttf"); 一般来说应该可以。请参阅我更新的答案的最后一部分。也许您会对 Eclipse 及其工作原理有更好的了解 :-)

以上是关于加载字体文件时的Eclipse Java文件FileInputStream与输入流[重复]的主要内容,如果未能解决你的问题,请参考以下文章

eclipse中的 res 和src文件夹是干啥的?

fgets读取文件时的注意事项

eclipse怎么设置xml文件字体大小

Java:字体不加载(来自外部文件)

如何打开用eclipse没有.project文件的Java工程

eclipse里面 左边项目里面文件夹显示的字体怎么改。我知道是在windows-preferences-general-apperance里