2

I am writing a function which needs to access a folder in the resources, and loop through all filenames, and if those match the criteria, those files are loaded.

new File(getClass.getResource("/images/sprites").getPath).listFiles()

returns a null pointer exception, where the directory tree follows Resources -> images -> sprites ->

Please can somebody point me in the right direction?

mysterymyster
  • 43
  • 1
  • 8

2 Answers2

6

Joop Eggen's answer is awesome, however it can only do one of two things:

  • Read the resources content when running from a IDE
  • Read the resources content when running the JAR via the command line

So here's a example (Kotlin, but it should be easy to migrate it to Java) that allows you to have both: Reading the resources content when running from a IDE or via the command line!

    val uri = MainApp::class.java.getResource("/locales/").toURI()
    val dirPath = try {
        Paths.get(uri)
    } catch (e: FileSystemNotFoundException) {
        // If this is thrown, then it means that we are running the JAR directly (example: not from an IDE)
        val env = mutableMapOf<String, String>()
        FileSystems.newFileSystem(uri, env).getPath("/locales/")
    }

    Files.list(dirPath).forEach {
        println(it.fileName)
        if (it.fileName.toString().endsWith("txt")) {
            println("Result:")
            println(Files.readString(it))
        }
    }
MrPowerGamerBR
  • 514
  • 1
  • 6
  • 14
5

A zip file system using jar:file: URIs would be something like this:

    URI uri = MainApp.class.getResource("/images/sprites").toURI();
    Map<String, String> env = new HashMap<>();
    try (FileSystem zipfs = FileSystems.newFileSystem(uri, env)) {
        //Path path = zipfs.getPath("/images/icons16");
        for (Path path : zipfs.getRootDirectories()) {
            Files.list(path.resolve("/images/sprites"))
                    .forEach(p -> System.out.println("* " + p));
        }
    }

Here I show getRootDirectories to possibly iterate over all resources.

Using the Files.copy one may copy them etcetera.


After comment of @MrPowerGamerBR:

The solution above deals with a jar. A more general solution, not exposing the jar character, is:

    URI uri = MAinApp.class.getResource("/images/sprites").toURI();
    Path dirPath = Paths.get(uri);
    Files.list(dirPath)
         .forEach(p -> System.out.println("* " + p));

(In fact one might even read lines from the directory itself, but this is the correct abstraction, using Path.)

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • 1
    This is a very clean solution but only works if you are running the application JAR itself, it doesn't work inside of a IDE. – MrPowerGamerBR Jan 13 '21 at 17:44
  • @MrPowerGamerBR ofcourse, the original answer looked for a problem (packing in jar), that is irrelevant when using the Path abstraction. Very good point. – Joop Eggen Jan 13 '21 at 18:02
  • 1
    oh wow I didn't expect a response that quick! The only reason I pointed that out is because your (original) answer was so clean compared to the hacks and workarounds that I found out in other SO threads, that I was a bit bummed that it didn't work inside of a IDE. I've tried your new answer and while that works inside of a IDE, it doesn't work running from inside the JAR. (But maybe it would be possible to check if it is running within a JAR and, if it is, use the other code! ...and it would still be more clean than the other workarounds people do to read files!) – MrPowerGamerBR Jan 13 '21 at 20:22
  • 1
    @MrPowerGamerBR Thanks for pointing out the limitation. As StackOverflow is not suited for too complex code, we'll leave it as an "exercise." – Joop Eggen Jan 13 '21 at 20:32
  • 1
    @KauêOliveira thanks for the criticism, I hope you also saw MrPowerGamerBR's nice answer. I was not willing to write something like that: there must be something more universal, and I did not have the time. I admit my pretext was just that. I promise betterment. – Joop Eggen May 11 '22 at 00:16
  • 1
    Little off-topic: please note, when using `Files.list`, to use it in `try-with-resource` block, as stream returned form `Files.list(Path)` needs to be closed. Hence: `try (var list = Files.list(dirPath)) { list.forEach(System.out::println); }` – Cromax Feb 21 '23 at 20:42