Search code examples
javajardesktop-applicationexecutable-jarjavafx-11

I can't load resources within a JAR even though I used getResourceAsStream


I want to load images from a JAR in a JavaFX app, but they don't show up even though it works in my IDE as expected. This is the output I get from load function file:/C:/Users/anass/OneDrive/Desktop/anass_coder/target/classes/com/anass/anass_code_editor/assets/languages/ and this is the absolute path of the images folder C:\Users\anass\OneDrive\Desktop\anass_coder\src\main\resources\com\anass\anass_code_editor\assets\languages

This is the load function:

public static void loadIconImages() {
    iconImages = new HashMap<>();
    URL imagesDirURL = App.class.getResource("/com/anass/anass_code_editor/assets/languages");
    if (imagesDirURL != null) {
        System.out.println(imagesDirURL.toExternalForm());
        try {
            InputStream imagesDirStream = imagesDirURL.openStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(imagesDirStream));
            String line;
            while ((line = reader.readLine()) != null) {
                String name = line.trim().toLowerCase();
                if (name.contains(".")) {
                    String resourcePath = "/com/anass/anass_code_editor/assets/languages/" + name;
                    InputStream imageStream = App.class.getResourceAsStream(resourcePath);

                    if (imageStream != null) {
                        iconImages.put(name.substring(0, name.indexOf(".")), new Image(imageStream));
                        imageStream.close();
                    }
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    } else {
        System.out.println("No dir");
    }
}

I am using Maven to bundle the desktop app.

I tried getResourceAsStream. The URI is not hierarchical. The error is gone, but resources are not found. The resources are well bundled and exist in the JAR archive.


Solution

  • You're asking for a directory name as a resource. This is unsupported and usually does not work. It should never work but the spec doesn't promise 'does not work' either. Point is, don't - it doesn't (always) work and is therefore useless. You then try to read this dir as a file (as a stream of bytes), hoping that somehow it will produce a list of file names. That doesn't work either. Or rather, it shouldn't - no spec claims that this is what must happen, and therefore, JVMs are free not to do that.

    The simple conclusion is that the concept of resource loading does not support any list commands whatsoever. This code wants to load 'every resource in a folder', that's a list operation and thus cannot be written without hackery.

    The common solution to this problem involves creating a file (that'd be a known name, so, can be read without a list primitive being available) that contains every file. You can then open the 'resource that contains a list' and then use that list - now you aren't relying on the loader system to list for you (which is good, because that's not something it can actually do).

    Of course, maintaining that 'file containing a list of image resources' is a pain. So, we fixed the problem but this fix introduces a new problem. One way to solve that problem is to have the file containing the names of all images be generated automatically during the build. Annotation processors can do it, so can build plugins. But, for now, first things first: Write out the list of all images by hand, stick it in a known place, read that file with .getResourcesAsStream, and use that to loop and load every image.

    Once that works you may want to ask a separate question showing that code and explaining that this works as is but that you're looking for a way to have the 'file containing the name of every image file' be generated automatically during builds.