4

I'm having trouble reaching a target folder "images" when my application is packaged as a JAR. I am not after getting a single file, what I want is a list of all the .jpg files in the "images" folder.

I've tried this: URI uri = getClass().getClassLoader.getResource("images").toURI();

This returns: jar:file:/C:/test-1.0-SNAPSHOT.jar!/BOOT-INF/lib/core-1.0-SNAPSHOT.jar!/images

Then i tried to create a stream to iterate over the files like this:

FileSystem fs = FileSystems.newFilesSystem(uri, Collections.emptyMap());
Path p = fs.getPath("images");
Files.walk(p).forEach(path -> System.out.println(path));

This just gives med a FileNotFoundException.

So, how can I access the "images" folder inside my nested JARs?

EDIT: This is not duplicate of: How to list the files inside a JAR file?. I have tried every thing in there. The difference is that i have a JAR INSIDE another JAR, nested JARs. This is what is creating the issue i believe.

mTv
  • 1,074
  • 17
  • 28

1 Answers1

4

Prior to JDK 12, there is no support for nested jar files in the builtin ZipFileSystem at all.

E.g., the newFileSystem method of ZipFileSystemProvider accepting a Path has a statement like

if (path.getFileSystem() != FileSystems.getDefault()) {
    throw new UnsupportedOperationException();
}

Even for JDK 12, getting hands on nested zip filesystems is not easy due to a consistent mistreatment of jar: URLs.

The syntax for jar: URLs has been specified as:

jar:<url>!/{entry}

which implies that the correct URL for your resource would be

outer file                file:/C:/test-1.0-SNAPSHOT.jar
nested jar file       jar:file:/C:/test-1.0-SNAPSHOT.jar!/BOOT-INF/lib/core-1.0-SNAPSHOT.jar
entry of nested   jar:jar:file:/C:/test-1.0-SNAPSHOT.jar!/BOOT-INF/lib/core-1.0-SNAPSHOT.jar!/images

To parse such a URL, you would have to extract the location of the jar file using the part between the first jar: and the last !/ and recursively parse that. However, all JDK code I’ve seen, regardless of backing the URL API or the FileSystem API consistently fails to do so, using indexOf("!/") rather then lastIndexOf("!/"). So nested jar: URIs/URLs simply don’t work.

The only way to get hands on a nested file system is to open them using Path instances already associated with a FileSystem:

Path outer = Paths.get("C:", "test-1.0-SNAPSHOT.jar");
ClassLoader cl = null;
try(FileSystem outerFs = FileSystems.newFileSystem(outer, cl);
    FileSystem innerFs = FileSystems.newFileSystem(
        outerFs.getPath("/BOOT-INF/lib/core-1.0-SNAPSHOT.jar"), cl) ) {

    Path p = innerFs.getPath("images");
    Files.walk(p).forEach(path -> System.out.println(path));
}

This works with JDK12 or newer.

Holger
  • 285,553
  • 42
  • 434
  • 765
  • Thanks, yes I've tried your last suggestion here on JDK 8, just throws a `ProviderNotFoundException` there:(. Made a workaround by listing the names of the files i need in a txt file instead. Its not pretty but it works – mTv Nov 22 '19 at 10:03
  • 1
    Well yes, `FileSystems.newFileSystem` will iterate over the installed providers, probe them and catch `UnsupportedOperationException`, continuing the iteration, to eventually say that no matching provider has been found. You could iterate over the providers manually, to find the right one and request it to create a new file system instead; that’s how I discovered that it throws an `UnsupportedOperationException`… – Holger Nov 22 '19 at 10:09