1

I want make a directory and all its files available to a method. The directory and files are located in

src/main/resources/images

and inside the library jar at runtime. I am reading the directory with

File directory = new File(getClass().getResource("/images").getFile());

Inspecting the contents of directory with

log.info("directory=" + directory.toString());
log.info("directory.isDirectory()=" + directory.isDirectory());
log.info("directory.isFile()=" + directory.isFile());
log.info("directory.canRead()=" + directory.canRead());

gives

  • directory=file:/home/leevilux/.m2/repository/groupid/library/1.0-SNAPSHOT/library-1.0-SNAPSHOT.jar!/images
  • directory.isDirectory()=false
  • directory.isFile()=false
  • directory.canRead()=false

So clearly it is not a directory, not a file and I can not read. But getFile did find something, because if I use getResource("/blabla") instead (which doesn't exist) then a null pointer expection is thrown (java.lang.NullPointerException).

Do I have to extract the jar to create "normal" paths? And if so, how?

Leevi L
  • 1,538
  • 2
  • 13
  • 28
  • what happens when you remove the leading slash -> `"images"`? – Lino Apr 10 '19 at 09:05
  • 1
    _Resources are not files_. You could provide another resource that lists the paths to all the images. Read the list of paths then call `getResource` for each listed resource. – Slaw Apr 10 '19 at 09:13
  • @Lino nullpointerexception – Leevi L Apr 10 '19 at 09:14
  • @Slaw I am calling `getFile` – Leevi L Apr 10 '19 at 09:15
  • The problem with treating resources like files and accessing them as such (e.g. `File` or `Path`) is that, when embedded in a JAR file, they are no longer "files"—they're entries in a JAR file. However, you could, when the scheme of the URL is `jar:`, use either the `java.util.jar` or the ZIP `FileSystemProvider` (NIO) API to list the contents of your "directory". – Slaw Apr 10 '19 at 09:34
  • 1
    Related: https://stackoverflow.com/questions/3923129/get-a-list-of-resources-from-classpath-directory?noredirect=1&lq=1 – Slaw Apr 10 '19 at 09:47

1 Answers1

1

Yes, you have to extract the resources you want to use. The is no other way. It is simple when you know exact file you want to use. You just save it and after that you can use it.

final InputStream directory = Main.class.getResourceAsStream("/images/name.jpg");
final byte[] buffer = new byte[directory.available()];
directory.read(buffer);
final File targetFile = new File("/tmp/target/name.jpg");
final OutputStream outStream = new FileOutputStream(targetFile);
outStream.write(buffer);

It is not so easy when you want to target a directory. You have to go through all entries of your jar and save all the ones you want to use. Then as in the case above they can be used. Something like the following example.

public class UnzipJar {
    public static void main(String[] args) throws IOException {
        final URL dirURL = UnzipJar.class.getResource("/images");
        final String jarPath = dirURL.getPath().substring(5, dirURL.getPath().indexOf("!"));

        extract("images", "/tmp/dest", jarPath);
    }

    public static void extract(final String source, final String destination, String jarPath) throws IOException {
        final File file = new File(jarPath);
        final JarFile jar = new JarFile(file);
        for (final Enumeration<JarEntry> enums = jar.entries(); enums.hasMoreElements(); ) {
            final JarEntry entry = enums.nextElement();
            final String fileName = destination + File.separator + entry.getName();
            final File f = new File(fileName);
            if (fileName.startsWith(source) && !fileName.endsWith("/")) {
                final InputStream is = jar.getInputStream(entry);
                final FileOutputStream fos = new FileOutputStream(f);
                while (is.available() > 0) {
                    fos.write(is.read());
                }
                fos.close();
                is.close();
            }
        }
    }
}
Januson
  • 4,533
  • 34
  • 42