Getting a directory inside a .jar
Asked Answered
H

8

8

I am trying to access a directory inside my jar file. I want to go through every of the files inside the directory itself. I tried, for example, using the following:

URL imagesDirectoryURL=getClass().getClassLoader().getResource("Images");

if(imagesFolderURL!=null)
{
    File imagesDirectory= new File(imagesDirectoryURL.getFile());
}

If I test this applet, it works well. But once I put the contents into the jar, it doesn't because of several reasons. If I use this code, the URL always points outside the jar, so I have to put the Images directory there. But if I use new File(imagesDirectoryURL.toURI());, it doesn't work inside the jar because I get the error URI not hierarchical. I am sure the directory exists inside the jar. How am I supposed the get the contents of Images inside the jar?

Horology answered 29/4, 2011 at 9:54 Comment(0)
H
5

Paths within Jars are paths, not actual directories as you can use them on a file system. To get all resources within a particular path of a Jar file:

  • Gain an URL pointing to the Jar.
  • Get an InputStream from the URL.
  • Construct a ZipInputStream from the InputStream.
  • Iterate each ZipEntry, looking for matches to the desired path.

..will I still be able to test my Applet when it's not inside that jar? Or will I have to program two ways to get my Images?

The ZipInputStream will not work with loose resources in directories on the file system. But then, I would strongly recommend using a build tool such as Ant to build (compile/jar/sign etc.) the applet. It might take an hour or so to write the build script & check it, but thereafter you can build the project by a few keystrokes and a couple of seconds.

It would be quite annoying if I always have to extract and sign my jar if I want to test my Aplet

I'm not sure what you mean there. Where does the 'extract' come into it? In case I was not clear, a sand-boxed applet can load resources this way, from any Jar that is mentioned in the archive attribute. Another thing you might do, is to separate the resource Jar(s) from the applet Jar. Resources typically change less than code, so your build might be able to take some shortcuts.

I think I really have to consider putting my Images into a seperate directory outside the jar.

If you mean on the server, there will be no practical way to get a listing of the image files short of help from the server. E.G. Some servers are insecurely set up to produce an HTML based 'file list' for any directory with no default file (such as an index.html).


I have only got one jar, in which my classes, images and sounds are.

OK - consider moving the sounds & images into a separate Jar. Or at the very least, put them in the Jar with 'no compression'. While Zip comression techniques work well with classes, they are less efficient at compressing (otherwise already compressed) media formats.

I have to sign it because I use the "Preferences" class to save user settings."

There are alternatives to the Preferences for applets, such as cookies. In the case of plug-in 2 architecture applet, you can launch the applet (still embedded in the browser) using Java Web Start. JWS offers the PersistenceService. Here is my small demo. of the PersistenceService.

Speaking of JWS, that brings me to: Are you absolutely certain this game would be better as an applet, rather than an app (e.g. using a JFrame) launched using JWS?

Applets will give you no end of stress, and JWS has offered the PersistenceService since it was introduced in Java 1.2.

Hahnemann answered 29/4, 2011 at 10:12 Comment(5)
If I use such a code, will I still be able to test my Applet when it's not inside that jar? Or will I have to program two ways to get my Images? It would be quiete annoying if I always have to extract and sign my jar if I want to test my Aplett :S If so, I think I really have to consider putting my Images into a seperate directory outside the jar...Horology
Firstly, my Application does currently works fine when I test it. But I changed some things in the structure. Before, I simply used "new ImageIcon(this.getClass().getClassLoader().getResource("anImage.jpg")" to get my images, from inside the jar. Now I built a class which handles loading them for me. This class loads all of the images at the start, so further loading time is unnecessary. By asking my question I simply wondered if there was an equally easy way to get "directories" from inside the jar.Horology
I have only got one jar, in which my classes, images and sounds are. So the jar file doesn't exist until I release another version or want to test it on another computer. I have to sign it because I use the "Preferences" class to save user settings. Referring to your second question: I haven't got any Client or Server, the Applett runs by itself. It is a game I am working on. I hope I made myself clearer now :)Horology
Thank you for all the tips. It looks like I will put my Media (Images, sounds) into another directory, and take a look at PersistenceServe. I thought I'd make it an Applet because it might be cool to be able to upload it and play it directly from the browser, but maybe I'll turn back into a JFrame.Horology
@Ava getClass().getClassLoader().getResource("/some/path/to/ResourceInJar.txt"); will get an URL to the resource in the Jar. The Jar's URL can be obtained from that first URL (examine the print out of the first URL). But.. Blah.class.getResourceAsStream(path) ..seriously, do you think I have a crystal ball?!? I have no idea even of the value of path. Post a new question with a minimal reproducible example as well as the description of the resource paths.Hahnemann
B
6

Here is a solution which should work given that you use Java 7... The "trick" is to use the new file API. Oracle JDK provides a FileSystem implementation which can be used to peek into/modify ZIP files, and that include jars!

Preliminary: grab System.getProperty("java.class.path", "."), split against :; this will give you all entries in your defined classpath.

First, define a method to obtain a FileSystem out of a classpath entry:

private static final Map<String, ?> ENV = Collections.emptyMap();

//

private static FileSystem getFileSystem(final String entryName)
    throws IOException
{
    final String uri = entryName.endsWith(".jar") || entryName.endsWith(".zip"))
        ? "jar:file:" + entryName : "file:" + entryName;
    return FileSystems.newFileSystem(URI.create(uri), ENV);
}

Then create a method to tell whether a path exists within a filesystem:

private static boolean pathExists(final FileSystem fs, final String needle)
{
    final Path path = fs.getPath(needle);
    return Files.exists(path);
}

Use it to locate your directory.

Once you have the correct FileSystem, use it to walk your directory using .getPath() as above and open a DirectoryStream using Files.newDirectoryStream().

And don't forget to .close() a FileSystem once you're done with it!

Here is a sample main() demonstrating how to read all the root entries of a jar:

public static void main(final String... args)
    throws IOException
{
    final Map<String, ?> env = Collections.emptyMap();
    final String jarName = "/opt/sunjdk/1.6/current/jre/lib/plugin.jar";
    final URI uri = URI.create("jar:file:" + jarName);
    final FileSystem fs = FileSystems.newFileSystem(uri, env);
    final Path dir = fs.getPath("/");
    for (Path entry : Files.newDirectoryStream(dir))
        System.out.println(entry);
}
Babb answered 15/3, 2014 at 1:30 Comment(0)
H
5

Paths within Jars are paths, not actual directories as you can use them on a file system. To get all resources within a particular path of a Jar file:

  • Gain an URL pointing to the Jar.
  • Get an InputStream from the URL.
  • Construct a ZipInputStream from the InputStream.
  • Iterate each ZipEntry, looking for matches to the desired path.

..will I still be able to test my Applet when it's not inside that jar? Or will I have to program two ways to get my Images?

The ZipInputStream will not work with loose resources in directories on the file system. But then, I would strongly recommend using a build tool such as Ant to build (compile/jar/sign etc.) the applet. It might take an hour or so to write the build script & check it, but thereafter you can build the project by a few keystrokes and a couple of seconds.

It would be quite annoying if I always have to extract and sign my jar if I want to test my Aplet

I'm not sure what you mean there. Where does the 'extract' come into it? In case I was not clear, a sand-boxed applet can load resources this way, from any Jar that is mentioned in the archive attribute. Another thing you might do, is to separate the resource Jar(s) from the applet Jar. Resources typically change less than code, so your build might be able to take some shortcuts.

I think I really have to consider putting my Images into a seperate directory outside the jar.

If you mean on the server, there will be no practical way to get a listing of the image files short of help from the server. E.G. Some servers are insecurely set up to produce an HTML based 'file list' for any directory with no default file (such as an index.html).


I have only got one jar, in which my classes, images and sounds are.

OK - consider moving the sounds & images into a separate Jar. Or at the very least, put them in the Jar with 'no compression'. While Zip comression techniques work well with classes, they are less efficient at compressing (otherwise already compressed) media formats.

I have to sign it because I use the "Preferences" class to save user settings."

There are alternatives to the Preferences for applets, such as cookies. In the case of plug-in 2 architecture applet, you can launch the applet (still embedded in the browser) using Java Web Start. JWS offers the PersistenceService. Here is my small demo. of the PersistenceService.

Speaking of JWS, that brings me to: Are you absolutely certain this game would be better as an applet, rather than an app (e.g. using a JFrame) launched using JWS?

Applets will give you no end of stress, and JWS has offered the PersistenceService since it was introduced in Java 1.2.

Hahnemann answered 29/4, 2011 at 10:12 Comment(5)
If I use such a code, will I still be able to test my Applet when it's not inside that jar? Or will I have to program two ways to get my Images? It would be quiete annoying if I always have to extract and sign my jar if I want to test my Aplett :S If so, I think I really have to consider putting my Images into a seperate directory outside the jar...Horology
Firstly, my Application does currently works fine when I test it. But I changed some things in the structure. Before, I simply used "new ImageIcon(this.getClass().getClassLoader().getResource("anImage.jpg")" to get my images, from inside the jar. Now I built a class which handles loading them for me. This class loads all of the images at the start, so further loading time is unnecessary. By asking my question I simply wondered if there was an equally easy way to get "directories" from inside the jar.Horology
I have only got one jar, in which my classes, images and sounds are. So the jar file doesn't exist until I release another version or want to test it on another computer. I have to sign it because I use the "Preferences" class to save user settings. Referring to your second question: I haven't got any Client or Server, the Applett runs by itself. It is a game I am working on. I hope I made myself clearer now :)Horology
Thank you for all the tips. It looks like I will put my Media (Images, sounds) into another directory, and take a look at PersistenceServe. I thought I'd make it an Applet because it might be cool to be able to upload it and play it directly from the browser, but maybe I'll turn back into a JFrame.Horology
@Ava getClass().getClassLoader().getResource("/some/path/to/ResourceInJar.txt"); will get an URL to the resource in the Jar. The Jar's URL can be obtained from that first URL (examine the print out of the first URL). But.. Blah.class.getResourceAsStream(path) ..seriously, do you think I have a crystal ball?!? I have no idea even of the value of path. Post a new question with a minimal reproducible example as well as the description of the resource paths.Hahnemann
R
2

You can use the PathMatchingResourcePatternResolver provided by Spring.

public class SpringResourceLoader {

    public static void main(String[] args) throws IOException {
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();

        // Ant-style path matching
        Resource[] resources = resolver.getResources("/Images/**");

        for (Resource resource : resources) {
            System.out.println("resource = " + resource);
            InputStream is = resource.getInputStream();
            BufferedImage img =  ImageIO.read(is);
            System.out.println("img.getHeight() = " + img.getHeight());
            System.out.println("img.getWidth() = " + img.getWidth());
        }
    }
}

I didn't do anything fancy with the returned Resource but you get the picture.

Add this to your maven dependency (if using maven):

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>3.1.2.RELEASE</version>
</dependency>

This will work directly from within Eclipse/NetBeans/IntelliJ and in the jar that's deployed.

Running from within IntelliJ gives me the following output:

resource = file [C:\Users\maba\Development\stackoverflow\Q12016222\target\classes\pictures\BMW-R1100S-2004-03.jpg]
img.getHeight() = 768
img.getWidth() = 1024

Running from command line with executable jar gives me the following output:

C:\Users\maba\Development\stackoverflow\Q12016222\target>java -jar Q12016222-1.0-SNAPSHOT.jar
resource = class path resource [pictures/BMW-R1100S-2004-03.jpg]
img.getHeight() = 768
img.getWidth() = 1024
Rrhagia answered 18/8, 2012 at 12:29 Comment(0)
T
0

I think you can directly access resources in ZIP/JAR file Please see Tutorial its giving solution to your question

How to extract Java resources from JAR and zip archives

Hopes that helps

Tricot answered 29/4, 2011 at 10:1 Comment(2)
Although that looks good, it's not exactly what I want. I simply put my whole Applet in a jar, not the Images in a seperate one. I could of course put my Images in a seperate directory (or, as it seems, a seperate jar), but isn't there a way to get them directly from inside my jar?Horology
Same just point to jar, which holds resources. not need to be sperateTricot
O
0

If I understand your problem you want to check the directory inside the jar and check all the files inside that directory.You can do something like:

JarInputStream jar = new JarInputStream(new FileInputStream("D:\\x.jar"));
    JarEntry jarEntry ;
    while(true)
        {
         jarEntry = jar.getNextJarEntry();
         if(jarEntry != null)
         {

            if(jarEntry.isDirectory() == false)
            {
        String str = jarEntry.getName();
                if(str.startsWith("weblogic/xml/saaj"))
        {
            anything which comes here are inside weblogic\xml\saaj directory
        }
        }

     }
    }    
Opposite answered 29/4, 2011 at 10:31 Comment(2)
Is there a way to easily apply this code to the jar where my code is in (Of course I don't know the exact path, because it always changes when I move the Applett around). Also, it will not work like this if I only want to test in Eclipse. Do I need two code sections then?Horology
In that case the jar name you can pass as a parameter. yeah, the directory path would change basing on the os unix/windows etc.Opposite
T
0

What you are looking for here might be the JarEntry list of the Jar... I had done some similar work during grad school... You can get the modified class here (http://code.google.com/p/marcellodesales-cs-research/source/browse/trunk/grad-ste-ufpe-brazil/ptf-add-on-dev/src/br/ufpe/cin/stp/global/filemanager/JarFileContentsLoader.java) Note that the URL contains an older Java class not using Generics...

This class returns a set of URLs with the protocol "jar:file:/" for a given token...

package com.collabnet.svnedge.discovery.client.browser.util;

import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class JarFileContentsLoader {

    private JarFile jarFile;

    public JarFileContentsLoader(String jarFilePath) throws IOException {
        this.jarFile = new JarFile(jarFilePath);
    }

    /**
     * @param existingPath an existing path string inside the jar.
     * @return the set of URL's from inside the Jar (whose protocol is "jar:file:/"
     */
    public Set<URL> getDirEntries(String existingPath) {
        Set<URL> set = new HashSet<URL>();
        Enumeration<JarEntry> entries = jarFile.entries();
        while (entries.hasMoreElements()) {
            String element = entries.nextElement().getName();
            URL url = getClass().getClassLoader().getResource(element);
            if (url.toString().contains("jar:file")
                    && !element.contains(".class")
                    && element.contains(existingPath)) {
                set.add(url);
            }
        }
        return set;
    }

    public static void main(String[] args) throws IOException {
        JarFileContentsLoader jarFileContents = new JarFileContentsLoader(
                "/u1/svnedge-discovery/client-browser/lib/jmdns.jar");
        Set<URL> entries = jarFileContents.getDirEntries("impl");
        Iterator<URL> a = entries.iterator();
        while (a.hasNext()) {
            URL element = a.next();
            System.out.println(element);
        }
    }

}

The output would be:

jar:file:/u1/svnedge-discovery/client-browser/lib/jmdns.jar!/javax/jmdns/impl/constants/
jar:file:/u1/svnedge-discovery/client-browser/lib/jmdns.jar!/javax/jmdns/impl/tasks/state/
jar:file:/u1/svnedge-discovery/client-browser/lib/jmdns.jar!/javax/jmdns/impl/tasks/resolver/
jar:file:/u1/svnedge-discovery/client-browser/lib/jmdns.jar!/javax/jmdns/impl/
jar:file:/u1/svnedge-discovery/client-browser/lib/jmdns.jar!/javax/jmdns/impl/tasks/
Tarbox answered 29/4, 2011 at 10:52 Comment(0)
D
0

May the following code sample can help you

   Enumeration<URL> inputStream = BrowserFactory.class.getClassLoader().getResources(".");
        System.out.println("INPUT STREAM ==> "+inputStream);
        System.out.println(inputStream.hasMoreElements());
        while (inputStream.hasMoreElements()) {
            URL url = (URL) inputStream.nextElement();
            System.out.println(url.getFile());
        }
Dobruja answered 29/4, 2011 at 11:11 Comment(0)
B
0

IF you really want to treat JAR files like directories, then please have a look at TrueZIP 7. Something like the following might be what you want:

URL url = ... // whatever
URI uri = url.toURI();
TFile file = new TFile(uri); // File-look-alike in TrueZIP 7
if (file.isDirectory) // true for regular directories AND JARs if the module truezip-driver-file is on the class path
    for (TFile entry : file.listFiles()) // iterate top level directory
         System.out.println(entry.getPath()); // or whatever

Regards, Christian

Bobbitt answered 3/5, 2011 at 11:12 Comment(1)
No, the TrueZIP File* API just requires JSE6.Bobbitt

© 2022 - 2024 — McMap. All rights reserved.