使用ClassLoader在静态方法中读取Java文件会出现FileNotFoundException问题

6
我想在我的Java类中读取一个文件。我的问题类似于这个问题,但有两个区别。首先,我使用了不同的项目布局:
/src/com/company/project /resources
在资源文件夹中,我有一个名为"test.txt"的文件:
/resources/test.txt
在项目文件夹中,我有一个名为test.java的类
/src/com/company/project/test.java
我希望我的Java类能够在静态方法中读取test.txt的内容。我已经尝试了以下方法:
private static String parseFile()
{
    try
    {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

        String fileURL = classLoader.getResource("test.txt").getFile();

        File file = new File(fileURL);


        ...
    }   
}

并且还有以下路径:

File file1 = new File("test.txt");
File file2 = new File("/test.txt");
File file3 = new File("/resources/test.txt");

然而,当我想要读取文件时,它们都会抛出FileNotFoundException异常。针对我的项目设置和方法需要是静态的这个事实,我应该如何正确声明文件路径?


2
resources文件夹是否在类路径上? - meskobalazs
我不知道在eclipse中如何检查这个?资源文件夹是一个源文件夹,就像src一样。我的eclipse IDE中的java源文件夹只有两个子文件夹:src和resources。也许这回答了问题? - user1884155
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - meskobalazs
5个回答

8

您应该使用与资源相同的JAR包中的类加载器,而不是TCCL。然后,您需要使用完整路径指定资源的名称。通常最好不要将其作为文件访问。直接打开进行读取(或如果需要,请将其复制到临时文件中):

InputStream is =
  Project.class.getClassLoader().getResourceAsStream("/resource/test.txt");

顺便提一下,如果你只想打开一个文件,你需要使用相对文件名。这是相对于起始目录搜索的,通常是项目主目录(在Eclipse中):

File resource = new File("resource/test.txt");

但是,如果您将其打包成JAR文件,则此方法将无法使用。

5

经过无数次尝试,我放弃了ClassLoader和任何形式的getResource方法。完全没有起作用,特别是如果是从另一个项目开始尝试的话。最终我总是得到了bin文件夹而不是src文件夹。所以我想出了以下解决方法:

public class IOAccessory {

    public static String getProjectDir() {
        try {
            Class<?> callingClass = Class.forName(Thread.currentThread().getStackTrace()[2].getClassName());
            URL url = callingClass.getProtectionDomain().getCodeSource().getLocation();
            URI parentDir = url.toURI().resolve("..");          
            return parentDir.getPath();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        return "";
    }
}

getProjectDir方法返回调用它的项目的物理路径,例如C:/workspace/MyProject/。 之后,您只需要将资源文件在MyProject中的相对路径连接起来即可打开流:

public void openResource() throws IOException {
    InputStream stream = null;
    String projectDir = IOAccessory.getProjectDir();
    String filePath = "resources/test.txt";
    try {
        stream = new FileInputStream(projectDir + filePath);
        open(stream);
    } catch(Exception e) {
        e.printStackTrace();
    } finally {
        if (stream != null)
            stream.close();
    }
}

无论openResource方法是静态的还是非静态的,以及它是从项目内部还是从构建路径上的另一个项目中调用,这种技术都可以使用。


0

这真的取决于你的IDE如何从项目中生成输出。通常,类加载器相对于调用类加载资源,但如果正确处理,“资源”将最终出现在输出文件夹层次结构的“根”中,并且您可以相应地访问它们。

例如,如果我在IntelliJ IDEA中重新创建您的代码,在名为com/acme/TestClass.class的类中,当构建时,IDE将生成以下输出结构。这假设我在一个名为“resources”的文件夹中放置了“test.txt”,并且该文件夹被指定为“资源根”:

/com
  /acme
    TestClass.class
test.txt

文本文件最终会出现在输出文件夹的根目录中,因此访问它非常简单。当我尝试在TestClass中的静态方法中加载文件时,以下代码对我有效:

ClassLoader cl = TestClass.class.getClassLoader();
InputStream is = cl.getResourceAsStream("test.txt");

0

我从静态函数中加载OpenGL ES的着色器。

请记住,您必须使用小写字母来命名文件和目录,否则操作将失败。

public class MyGLRenderer implements GLSurfaceView.Renderer {

    ...

    public static int loadShader() {
        //    Read file as input stream
        InputStream inputStream = MyGLRenderer.class.getResourceAsStream("/res/raw/vertex_shader.txt");

        //    Convert input stream to string
        Scanner s = new Scanner(inputStream).useDelimiter("\\A");
        String shaderCode = s.hasNext() ? s.next() : "";
    }

    ...

}

将输入流转换为字符串的另一种方法。

byte[] bytes;
String shaderCode = "";
try {
    bytes = new byte[inputStream.available()];
    inputStream.read(bytes);
    shaderCode = new String(bytes);
}
catch (IOException e) {
    e.printStackTrace();
}

使用stream.available()是有风险的,它不能保证返回流的全部或任何长度。 - eckes

0
其他答案没有涉及到的唯一问题是,您的URL转换为文件可能无法正常工作。如果您项目上面的目录包含必须解码的字符,则调用“getResource(“test.txt”).getFile()”将不会给您一个有效的java.io.File路径。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接