- Java Tip 49: How to extract Java resources from JAR and zip archives
- Loading a GIF image
- A note on naming
- How it works
- Exercises for the reader
- Conclusion
- Saved searches
- Use saved searches to filter your results more quickly
- License
- CGJennings/jar-loader
- Name already in use
- Sign In Required
- Launching GitHub Desktop
- Launching GitHub Desktop
- Launching Xcode
- Launching Visual Studio Code
- Latest commit
- Git stats
- Files
- README.md
- About
Java Tip 49: How to extract Java resources from JAR and zip archives
Most Java programmers are pretty clear on the advantages of using a JAR file to bundle up all of the various resources (that is, .class files, sounds, and images) that comprise their Java solution. (If you’re not familiar with JAR files, check out the Resources section below.) A very common question asked by people who are just starting to incorporate JAR files into their bag of tricks is, «How do I extract an image from a JAR?» We’re going to answer that question and provide a class to make it super simple to extract any resource from a JAR!
Loading a GIF image
Let’s say we have a JAR file containing a bunch of .gif image files that we want to use in our application. Here’s how we could access an image file from the JAR using the JarResources:
JarResources jar = new JarResources ("Images.jar"); Image logo = Toolkit.getDefaultToolkit().createImage (jar.getResource ("logo.gif");
That code snippet shows that we can create a JarResources object initialized to the JAR file containing the resource that we’re interested in using — Images.jar . We then use the JarResources’ getResource() method to provide the raw data from the logo.gif file for the AWT toolkit’s createImage() method.
A note on naming
The JarResource is a reasonably straightforward example of how to use various facilities provided by Java 1.1 to manipulate JAR and zip archive files.
A quick note about naming. Archiving support in Java actually started out using the popular zip archiving format (check out «Java Tip 21: Use archive files to speed up applet loading»). So, originally, in implementing Java support to manipulate the archive files, all of the classes and whatnot were placed in the java.util.zip package; these classes tend to start with » Zip .» But somewhere in the move to Java 1.1, the powers that be changed the name of the archive to be more Java focused. Hence, what we now call JAR files basically are zip files.
How it works
The important data fields for the JarResources class are used to track and store the contents of the specified JAR file:
public final class JarResources < public boolean debugOn=false; private Hashtable htSizes=new Hashtable(); private Hashtable htJarContents=new Hashtable(); private String jarFileName;
So, the instantiation of the class sets the name of the JAR file and then calls off to the init() method to do all of real work:
public JarResources(String jarFileName)
Now, the init() method pretty much just loads in the entire contents of the specified JAR file into a hashtable (accessed via the name of the resource).
This is a fairly hefty method, so let's break it down a bit further. The ZipFile class gives us basic access to the JAR/zip archive header information. This is similar to the directory information in a file system. Here we enumerate through all of the entries in the ZipFile and build out the htSizes hashtable with the size of each resource in the archive:
private void init() < try < ZipFile zf=new ZipFile(jarFileName); Enumeration e=zf.entries(); while (e.hasMoreElements()) < ZipEntry ze=(ZipEntry)e.nextElement(); if (debugOn) < System.out.println(dumpZipEntry(ze)); >htSizes.put(ze.getName(),new Integer((int)ze.getSize())); > zf.close();
Next, we access the archive through the use of the ZipInputStream class. The ZipInputStream class does all of the magic to allow us to read each of the individual resources in the archive. We read the exact number of bytes from the archive that comprise each resource and store that data into the htJarContents hashtable accessible by resource name:
FileInputStream fis=new FileInputStream(jarFileName); BufferedInputStream bis=new BufferedInputStream(fis); ZipInputStream zis=new ZipInputStream(bis); ZipEntry ze=null; while ((ze=zis.getNextEntry())!=null) < if (ze.isDirectory()) < continue; >if (debugOn) < System.out.println( "ze.getName()="+ze.getName()+","+"getSize()="+ze.getSize() ); >int size=(int)ze.getSize(); // -1 means unknown size. if (size==-1) < size=((Integer)htSizes.get(ze.getName())).intValue(); >byte[] b=new byte[(int)size]; int rb=0; int chunk=0; while (((int)size - rb) > 0) < chunk=zis.read(b,rb,(int)size - rb); if (chunk==-1) < break; >rb+=chunk; > // add to internal resource hashtable htJarContents.put(ze.getName(),b); if (debugOn) < System.out.println( ze.getName()+" rb="+rb+ ",size="+size+ ",csize="+ze.getCompressedSize() ); >> > catch (NullPointerException e) < System.out.println("done."); >catch (FileNotFoundException e) < e.printStackTrace(); >catch (IOException e) < e.printStackTrace(); >>
Note that the name used to identify each resource is the qualified path name of the resource in the archive, not, for example, the name of a class in a package -- that is, the ZipEntry class from the java.util.zip package would be named "java/util/zip/ZipEntry," rather than "java.util.zip.ZipEntry."
The final important part of the code is the simple test driver. The test driver is a simple application that takes a JAR/zip archive name and the name of a resource. It tries to find the resource in the archive and reports its success or failure:
public static void main(String[] args) throws IOException < if (args.length!=2) < System.err.println( "usage: java JarResources " ); System.exit(1); > JarResources jr=new JarResources(args[0]); byte[] buff=jr.getResource(args[1]); if (buff==null) < System.out.println("Could not find "+args[1]+"."); >else < System.out.println("Found "+args[1]+ " (length="+buff.length+")."); >> > // End of JarResources class.
And there you have it. A simple-to-use class that hides all of the messiness involved with using resources tucked away in JAR files.
Exercises for the reader
Now that you have a feel for extracting resources from an archive file, here are some directions that you may want to explore in modifying and extending the JarResources class:
- Instead of loading everything during construction, do delayed loading. In the case of a big JAR file, there may not be enough memory to load all of the files during construction.
- Instead of simply providing a generic accessor method like getResource() , we could provide other resource-specific accessors -- for example, getImage() , which returns a Java Image object, getClass() , which returns a Java Class object (with help from a customized class loader), and so on. If the JAR file is small enough, we could pre-build all resources based on their extensions (.gif, .class, and so on).
- Some methods should provide information about the given JAR file itself (basically a wrapper around ZipFile ), including: the number of Jar/zip entries; an enumerator that returns all the names of resources; accessors that return the length (and other attributes) of a particular entry; and an accessor that allows indexing, to name a few.
- JarResources can be extended to be used by applets. By utilizing applet parameters and the URLConnection class, the JAR content can be downloaded from the network instead of opening the archives as local files. Furthermore, we may extend this class as a custom Java content handler.
Conclusion
If you've been eager to know how to extract an image from a JAR file, now you've got a way. Not only can you handle images with a JAR file, but with the new class provided in this tip, you work your extracting magic on any resource from a JAR.
Arthur Choi currently works for IBM as an advisory programmer. He has worked for several companies, including SamSung Network Laboratory and MITRE. The various projects that he has worked on are client/server systems, distributed object computing, and network management. He has used a number of languages in various operating system environments. He started programming in 1981 with FORTRAN IV and COBOL. Later, he switched to C and C++, and he has been working with Java for about two years. He's most interested in applications of Java in the areas of data repositories through wide-area networks, and parallel and distributed processing through the Internet (using agent-based programming). John Mitchell, an employee, consultant, and principal of his own company, has invested the last ten years in development of cutting-edge computer software, and in advising and training other developers. He has provided consulting on Java technology, compilers, interpreters, Web-based applications, and Internet commerce. John co-authored Making Sense of Java: A Guide for Managers and the Rest of Us and has published articles in programming journals. In addition to writing the Java Tips column for JavaWorld, he moderates the comp.lang.tcl.announce and comp.binaries.geos newsgroups.
This story, "Java Tip 49: How to extract Java resources from JAR and zip archives" was originally published by JavaWorld .
Next read this:
Copyright © 1998 IDG Communications, Inc.
Saved searches
Use saved searches to filter your results more quickly
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session.
Easily fix apps that dynamically load JAR files under Java 9+
License
CGJennings/jar-loader
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Sign In Required
Please sign in to use Codespaces.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching Xcode
If nothing happens, download Xcode and try again.
Launching Visual Studio Code
Your codespace will open once ready.
There was a problem preparing your codespace, please try again.
Latest commit
Git stats
Files
Failed to load latest commit information.
README.md
Easy migration to Java 9+ for applications that extend the class path by reflective access to the class loader.
Does your code work under Java 8 but fail under Java 9+ with an error stating that the system class loader is not an instance of URLClassLoader or that the method addURL could not be found? Then this is for you.
- Add jar-loader.jar to your build libraries and application class path.
- Add -javaagent:path/to/jar-loader.jar to the VM options passed to the java command used to start your application.
- Where your code previously used reflection to invoke addURL on the system class loader, instead call ca.cgjennings.jvm.JarLoader.addToClassPath(File jarFile) .
Jar Loader uses an official, documented method for extending the class path at runtime that has been supported since Java 6. It falls back to attempting reflective access if that fails. This method differs from URLClassLoader in that it only supports local JAR files. If your code relies on addURL to download code over a network, you will have to download it yourself and write it to a local file.
If you move the compiled JarLoader class to a different JAR file, it must include a MANIFEST.MF with the following line:
Image credit: icon adapted from an icon by Eucalyp.
About
Easily fix apps that dynamically load JAR files under Java 9+