摘要:
将Java源文件打包成JAR文件,可以减少下载时、增强安全性和易管理性。本文将讨论如何从JAR文件中提取Java源文件。绝大多数Java程序员非常善于利用JAR文件的优势将各种包含Java解决方案的资源进行打包。面对JAR文件,人们询问的一个普遍问题是:“我何从一个JAR文件中
提取出一个图象文件?”。本文将回答这个问题,并且提供一个Java类,它可以容易地从一个JAR文件中析取出任意一个文件。加载一个GIF文件
假设我们有一个包含一些.GIF图象文件的JAR文件,这些图象文件将在应用程序中被使用。以下是我们使用JarResources类从JAR文件中析取一个图象文件的代码:JarResourcesjar=newJarResources("Images.jar");
Imagelogo=
Toolkit.getDefaultToolkit().createImage(jar.getResource("logo.gif");上面代码的含义是:我们建立一个JavaResources对象,并将它初始化为一个包含我们感兴趣资源的JAR文件--Images.jar。然后我们使用JavaResources类的getResource()方法从logo.gif取出原始数据,然后调用AWT工具箱的createImage()方法从原始数据建立一个Image对象实例。有关命名的注释JarResource是一个相当容易理解的例子,它解释了如何使用Java1.1提供的各种功能处理JAR和ZIP压缩文档文件。关于命名的的简单解释。Java对压缩文档的支持实际上起源于使用一般的ZIP压缩文档格式。因此,Java中实现压缩档案操作的类都被放入java.util.zip包中;这些
类一般以“Zip.”开始。但是Java升级到了1.1版以后,压缩文档的命名变得以Java为中心了。从此,我们所说的JAR压缩文档基本上还是zip文件。代码具体的工作流程
JavaResources类中的一些重要字段用来跟踪和存储指定JAR文件的内容。publicfinalclassJarResources{
publicbooleandebugOn=false;
privateHashtablehtSizes=newHashtable();
privateHashtablehtJarContents=newHashtable();
privateStringjarFileName;在类进行实例化时设置JAR的文件名,然后调用init()方法完成所有的初始化工作。publicJarResources(StringjarFileName){
this.jarFileName=jarFileName;
init();
}现在,init()方法把指定JAR文件的全部内容装入到一个杂凑表(hashtable)中(杂凑表名可以从资源名进行访问)
init()是一个功能相当强大的方法,让我们来逐步理解它的功能。ZipFile类使我们基本上能访问JAR/Zip压缩文档的头部信息。这和一个文件系统的目录信息相似。在这里我们列出Zip文件的所有条目(entry),并且按照文档中的每个资源的尺寸创建htSizes杂凑表(hashtable)。privatevoidinit(){try{
ZipFilezf=newZipFile(jarFileName);
Enumeratione=zf.entries();
while(e.hasMoreElements()){
ZipEntryze=(ZipEntry)e.nextElement();
if(debugOn){
System.out.println(dumpZipEntry(ze));
}
htSizes.put(ze.getName(),newInteger((int)ze.getSize()));
}
zf.close();下一步,我们使用ZipInputStream类访问压缩文档。ZipInputStream类完成了所有的工作以使我们能读出压缩文档中任意一个资源。我们从包含每个资源的压缩文档中读出准确数目的字节,并将它们存储在一个可由资源名访问的htJarContents杂凑表中。FileInputStreamfis=newFileInputStream(jarFileName);
BufferedInputStreambis=newBufferedInputStream(fis);
ZipInputStreamzis=newZipInputStream(bis);
ZipEntryze=null;
while((ze=zis.getNextEntry())!=null){
if(ze.isDirectory()){
continue;
}
if(debugOn){
System.out.println(
"ze.getName()="+ze.getName()+","+"getSize()="+ze.getSize()
);
}
intsize=(int)ze.getSize();
//-1meansunknownsize.
if(size==-1){
size=((Integer)htSizes.get(ze.getName())).intValue();
}byte[]b=newbyte[(int)size];
intrb=0;
intchunk=0;
while(((int)size-rb)>0){
chunk=zis.read(b,rb,(int)size-rb);
if(chunk==-1){
break;
}
rb+=chunk;
}
//addtointernalresourcehashtable
htJarContents.put(ze.getName(),b);
if(debugOn){
System.out.println(
ze.getName()+"rb="+rb+
",size="+size+
",csize="+ze.getCompressedSize()
);
}
}
}catch(NullPointerExceptione){
System.out.println("done.");
}catch(FileNotFoundExceptione){
e.printStackTrace();
}catch(IOExceptione){
e.printStackTrace();
}
}注意,用来确定每个资源的名字是压缩文档中资源的实际路径名,而不是包(package)中类的名字。也就是说,java.util.zip包中的ZipEntry类的名字应为“java/util
/zip.ZipEntry”,而不是"java.util.zip.ZipEntry"。
这段代码最后一个重要部分是一个简单测试程序。这个测试程序能提取一个压缩文档名和资源名。它寻找压缩文档中的资源并报告运行成功与否。publicstaticvoidmain(String[]args)throwsIOException{
if(args.length!=2){
System.err.println(
"usage:javaJarResources"
);
System.exit(1);
}
JarResourcesjr=newJarResources(args[0]);
byte[]buff=jr.getResource(args[1]);
if(buff==null){
System.out.println("Couldnotfind"+args[1]+".");
}else{
System.out.println("Found"+args[1]+"(length="+buff.length+").");
}
}
}//EndofJarResourcesclass.现在你知道怎么做了。这个易于使用的类隐藏了所有使用压缩文档中的资源的复杂实现细节。结论
如果你急于想知道怎样从一个JAR压缩文档中解出一个图象,那么现在你已拥有了一种方法。你不仅能处理和JAR文件相关的图象,而且你还能使用这篇文章提供了新类,在JAR中的任何资源上表演你的解压魔术。