Extracting files from Jar is more efficient

I am expanding a utility class that combines a set of images and .xml description files. Currently, I store all the files in a directory and download them from there. The directory is as follows:

8.png 8.xml 9.png 9.xml 10.png 10.xml ... ... 50.png 50.xml ... 



Here is my current constructor. He is lightning fast and does what I need. (I removed the error checking part to make it easier to read):

 public DivineFont(String directory ) { File dir = new File(directory); //children is an array that looks like this: '10.fnt', '11.fnt', etc. String[] children = dir.list(fntFileFilter); fonts = new Hashtable<Integer, AngelCodeFont>(100); AngelCodeFont buffer; int number; String fntFile; String imgFile; for(int k = 0; k < children.length; k++ ) { number = Integer.parseInt( children[k].split("\\.")[0] ); fntFile = directory + File.separator + number + ".xml"; imgFile = directory + File.separator + number + ".png"; buffer = new AngelCodeFont(fntFile, imgFile); fonts.put(number, buffer); } } 

For the sake of web start and cleanliness, I am trying to download these resources from the Jar. It works for me, but the download time is instant to several seconds, and this is unacceptable. Here is the code I tried (again, error checking was stripped):

(This is not the best way to do what I want to do, it’s a layout to see if this idea works. It’s not. Two for-loops are by no means the source of the problem, it’s the process of creating all those InputStream that slow it down )

 public DivineFont(String jarFileName ) { JarFile jarfile = new JarFile(jarFileName); Enumeration<JarEntry> em = jarfile.entries(); ArrayList<Integer> fontHeights = new ArrayList<Integer>(100); for (Enumeration em1 = jarfile.entries(); em1.hasMoreElements(); ) { String fileName = em1.nextElement().toString(); if( fileName.endsWith(".fnt") ) { fontHeights.add( Integer.parseInt(fileName.split("\\.")[0] ) ); } } fonts = new Hashtable<Integer, AngelCodeFont>(100); AngelCodeFont buffer; int number; for(int k = 0; k < fontHeights.size(); k++ ) { number = fontHeights.get(k); InputStream fntFileStream = jarfile.getInputStream(jarfile.getEntry(number + ".xml")); InputStream pngFileStream = jarfile.getInputStream(jarfile.getEntry(number + ".png")); buffer = new AngelCodeFont(String.valueOf(number), fntFileStream, pngFileStream ); fonts.put(number, buffer); } } 


Does anyone know how best to work with .jar files, besides how I tried here? Here's the AngelCodeFont API . If it were absolutely necessary, I could write a patch for this, but I would not want this. It seems to me that there is probably a way to do what I want to do, I'm just not familiar with this.

I am not terribly opposed to quickly dumping the jar into a temporary directory and then reading files from there, but if there is a way to do this by reading directly from the jar, I would rather do it.

Also: compression is not a problem at all. The only reason I use the can is because of the packaging problem.

+2
source share
4 answers

Well, I found the answer. The answer to my original question was "Wrong question." The Jar file was not a problem, it was a library that I used to upload images.

When I booted from the file system, the image was called "38.png", etc. When I booted from Jar, I just called it "38".

The Image loader class inside the library uses the file name extension to determine which image loader to use. If the file extension is missing, it uses the slower base image downloader. When I changed this line:

 buffer = new AngelCodeFont(String.valueOf(number), fntFileStream, pngFileStream ); 

to this line:

 buffer = new AngelCodeFont( number + ".png", fntFileStream, pngFileStream ); 

We have a winner.

Thanks for the help anyway guys. It took me a few hours to figure this out, but if it weren’t for your posts, I would probably continue to believe that Java was to blame, not the library that I use.

+1
source

Opening a JAR is a very expensive operation. That way you can open the JAR once and save the JarFile instance in a static field somewhere. In your case, you will also want to read all the records and save them in hashmap for instant access to the resource you need.

Another solution is to put the JAR in the classpath and use

 DivineFont.class.getContextClassLoader().getResource*("/8.png"); 

Pay attention to "/"! If you omit it, Java will search in the same directory (package) in which it finds the DivineFont.class file.

Getting things from the classpath has been optimized to death in Java.

+4
source

Processing Jar files can be very expensive, and the Java class library already implements such resource loading (possibly as efficiently as possible).

Plus, once you get to webstart, your jar file names will be corrupted, so you probably have to examine each jar file on the class path to load your resources (I did it!).

Use Class.getResourceAsStream (String resourceName) instead. I did not profile it, but I did not notice that it is noticeably slower than direct access to files.

0
source

This library may not compress as much as you need, but it will be faster ...

-2
source

All Articles