views:

1803

answers:

8

I have this code which reads all the files from a directory.

    File textFolder = new File("text_directory");

    File [] texFiles = textFolder.listFiles( new FileFilter() {
           public boolean accept( File file ) {
               return file.getName().endsWith(".txt");
           }
    });

Works great. It fills the array with all the files that end with ".txt" from directory "text_directory"

Question:

How can I read in a similar fashion the contents of a directory within a jar file?

So what I really want to do is list in a similar fashion all the images inside my jar, so I can load them with:

ImageIO.read(this.getClass().getResource("CompanyLogo.png"));

That one works because the "CompanyLogo" is "hardcoded" but the number of images inside the jar could be from 10 to 200.

EDIT

So I guess my main problem would be, how to know the name of the jar where my main class lives.

Granted I could read it using java.util.Zip

My Structure is like this:

They are like:

 
    my.jar!/Main.class
    my.jar!/Aux.class
    my.jar!/Other.class
    my.jar!/images/image01.png
    my.jar!/images/image02a.png
    my.jar!/images/imwge034.png
    my.jar!/images/imagAe01q.png
    my.jar!/META-INF/manifest 

Right now I'm able to load for instance "images/image01.png" using:

    ImageIO.read(this.getClass().getResource("images/image01.png));

But only because I know the file name, for the rest I have to load them dinamically.

Thanks in advance.

+2  A: 

A jar file is just a zip file with a structured manifest. You can open the jar file with the usual java zip tools and scan the file contents that way, inflate streams, etc. Then use that in a getResourceAsStream call, and it should be all hunky dory.

EDIT / after clarification

It took me a minute to remember all the bits and pieces and I'm sure there are cleaner ways to do it, but I wanted to see that I wasn't crazy. In my project image.jpg is a file in some part of the main jar file. I get the class loader of the main class (SomeClass is the entry point) and use it to discover the image.jpg resource. Then some stream magic to get it into this ImageInputStream thing and everything is fine.

InputStream inputStream = SomeClass.class.getClassLoader().getResourceAsStream("image.jpg");
JPEGImageReaderSpi imageReaderSpi = new JPEGImageReaderSpi();
ImageReader ir = imageReaderSpi.createReaderInstance();
ImageInputStream iis = new MemoryCacheImageInputStream(inputStream);
ir.setInput(iis);
....
ir.read(0); //will hand us a buffered image
Mikeb
This jar contains the main program and the resources. How do I refer to the self jar? from within the jar file?
OscarRyz
To refer to the JAR file, just use "blah.JAR" as the String. You can use `new File("blah.JAR")` to create a File object that represents the JAR, for example. Just replace "blah.JAR" with the name of your JAR.
Thomas Owens
If its the same jar that you are already running out of, the class loader should be able to see stuff inside the jar ... I misunderstood what you were trying to do initially.
Mikeb
Well yes, I already have that, the problem is when I need something like: "...getResourceAsStream("*.jpg"); ... " That is, dynamically, list the files contained.
OscarRyz
+1  A: 

Given an actual jar file, you can list the contents using JarFile.entries(). You will need to know the location of the jar file though - you can't just ask the classloader to list everything it could get at.

You should be able to work out the location of the jar file based on the URL returned from ThisClassName.class.getResource("ThisClassName.class") but it may be a tiny bit fiddly.

Jon Skeet
Reading your answer another question raised. What would yield the call: this.getClass().getResource("/my_directory"); It should return an URL that could in turn be .... used as directory? Nahh... let me try it.
OscarRyz
You always know the location of the JAR - it's in ".". As long as the name of the JAR is known to be something, you can use a String constant somewhere. Now, if people go changing the name of the JAR...
Thomas Owens
@Thomas: That's assuming you're running the app from the current directory. What's wrong with "java -jar foo/bar/baz.jar"?
Jon Skeet
I believe (and would have to verify), that if you had code in your Jar that was `new File("baz.jar)`, the File object would represent your JAR file.
Thomas Owens
@Thomas: I don't believe so. I believe it's would be relative to the current working directory of the process. I'd have to check too though :)
Jon Skeet
+1  A: 

Here's a method I wrote for a "run all JUnits under a package". You should be able to adapt it to your needs.

private static void findClassesInJar(List<String> classFiles, String path) throws IOException {
    final String[] parts = path.split("\\Q.jar\\\\E");
    if (parts.length == 2) {
        String jarFilename = parts[0] + ".jar";
        String relativePath = parts[1].replace(File.separatorChar, '/');
        JarFile jarFile = new JarFile(jarFilename);
        final Enumeration<JarEntry> entries = jarFile.entries();
        while (entries.hasMoreElements()) {
            final JarEntry entry = entries.nextElement();
            final String entryName = entry.getName();
            if (entryName.startsWith(relativePath)) {
                classFiles.add(entryName.replace('/', File.separatorChar));
            }
        }
    }
}

Edit: Ah, in that case, you might want this snippet as well (same use case :) )

private static File findClassesDir(Class<?> clazz) {
    try {
        String path = clazz.getProtectionDomain().getCodeSource().getLocation().getFile();
        final String codeSourcePath = URLDecoder.decode(path, "UTF-8");
        final String thisClassPath = new File(codeSourcePath, clazz.getPackage().getName().repalce('.', File.separatorChar));
    } catch (UnsupportedEncodingException e) {
        throw new AssertionError("impossible", e);
    }
}
Ran Biron
I guess the big problem is to know the jar file name in first place. It is the jar where the Main-Class: lives.
OscarRyz
+5  A: 
CodeSource src = MyClass.class.getProtectionDomain().getCodeSource();
if (src != null) {
  URL jar = src.getLocation();
  ZipInputStream zip = new ZipInputStream(jar.openStream());
  /* Now examine the ZIP file entries to find those you care about. */
  ...
} 
else {
  /* Fail... */
}
erickson
hey thanks... been looking for a way to do this for a few hours now !!
Newtopian
A: 

Given that you have created the jar, you might as well include the list of files within it rather than attempting any tricks.

Tom Hawtin - tackline
A: 

So I guess my main problem would be, how to know the name of the jar where my main class lives.

Assuming that your project is packed in a Jar (not necessarily true!), you can use ClassLoader.getResource() or findResource() with the class name (followed by .class) to get the jar that contains a given class. You'll have to parse the jar name from the URL that gets returned (not that tough), which I will leave as an exercise for the reader :-)

Be sure to test for the case where the class is not part of a jar.

Kevin Day
huh - interesting that this would have been down moded without comment... We use the above technique all the time and it works just fine.
Kevin Day
An old issue, but to me this seems like a fine hack. Upvoted back to zero :)
Tuukka Mustonen
+1  A: 

erickson's answer worked perfectly:

Here's the working code.

CodeSource src = MyClass.class.getProtectionDomain().getCodeSource();
List<String> list = new ArrayList<String>();

if( src != null ) {
    URL jar = src.getLocation();
    ZipInputStream zip = new ZipInputStream( jar.openStream());
    ZipEntry ze = null;

    while( ( ze = zip.getNextEntry() ) != null ) {
        String entryName = ze.getName():
        if( entryName.startsWith("images") &&  entryName.endsWith(".png") ) {
            list.add( entryName  );
        }
    }

 }
 webimages = list.toArray( new String[ list.size() ] );

And I have just modify my load method from this:

File[] webimages = ... 
BufferedImage image = ImageIO.read(this.getClass().getResource(webimages[nextIndex].getName() ));

To this:

String  [] webimages = ...

BufferedImage image = ImageIO.read(this.getClass().getResource(webimages[nextIndex]));
OscarRyz
A: 

Hi all,

there are two very useful utilities both called jarscan :

www.inetfeedback.com/jarscan

jarscan.dev.java.net

see also this question : stackoverflow.com/questions/759923/jarscan-scan-all-jar-files-in-all-subfolders-for-specific-class

jgran

jgran