简体   繁体   中英

Java read file within static method, using ClassLoader gives FileNotFoundException

I want to read a file in my java class. My question is similar to this one , but there are two differences. first, I use a different project layout:

/src/com/company/project
/resources

In the resources folder I have a file called "test.txt":

/resources/test.txt

In the project folder I have a class test.java

/src/com/company/project/test.java

I want mu java class to be able to read the contents of test.txt in a STATIC METHOD. I've tried the following:

private static String parseFile()
{
    try
    {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

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

        File file = new File(fileURL);


        ...
    }   
}

and the following paths:

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

But they all throw a FileNotFoundException when I want to read the file. How can I correctly declare the path to my file in the snippet above with respect to my project setup and the fact that the method needs to be static?

After endless trials, I gave up on ClassLoader and getResource methods of any kind. Absolutely nothing worked, especially if the opening attempt was made from another project. I always ended up getting the bin folder instead of the src folder. So I devised the following work around:

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

The getProjectDir method returns the physical path of the project from which it was called, eg C:/workspace/MyProject/. After that, all you need to do is concatenate the relative path in MyProject of your resource file to open the stream:

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();
    }
}

This technique works whether the openResource method is static or non-static, and whether it is called from within the project or from another project on the build path.

You should use the class loader of the class which is in the same JAR as the resource instead of the TCCL. And then you need to specify the name of the resource with a full path. And it is typically not good to access those as files. Just open it directly for read (or copy it to a temp file if you need to):

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

BTW: if you simply want to open a file, you need to use a relative file name. This is searched relative to the start dir, which is normally the project main dir (in eclipse):

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

(but this wont work if you package it up as a JAR).

It really depends on how your IDE generates output from your project. Typically, classloaders load resources relative to the invoking classes, but if treated right, 'resources' will just end up in the 'root' of your output folder hierarchy, and you can access them accordingly.

For example, if I recreate your code in IntelliJ IDEA, in a class called com/acme/TestClass.class , the following output structure is generated within the IDE when building. This assumes I have "test.txt" sitting in a folder I called "resources", and that folder is specified as being a "resources root":

/com
  /acme
    TestClass.class
test.txt

The text file ends up in the output folder's root, so accessing it is simple. The following code works for me when I attempt to load the file in a static method within TestClass :

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

The only thing not covered in the other answers is that your URL conversion to file might not work correctly. If the directories above your project contain a characters that must be decoded then your call to 'getResource("test.txt").getFile()' is not giving you a valid java.io.File path.

I load shader for openGL ES from static function.

Remember you must use lower case for your file and directory name, or else the operation will be failed

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() : "";
    }

    ...

}

Another method to convert input stream to string.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM