多數 java 程式員都非常清楚使用 JAR 檔案将組成 java 解決方案的各種資源(即 .class 檔案、聲音和圖像)打包的優點。(如果您不熟悉 JAR 檔案,請參閱後文的 參考資源部分。)剛開始使用 JAR 檔案的人常問的一個問題是:“如何從 JAR 檔案中提取圖像呢?”本文将回答這個問題,并會提供一個類,這個類使從 JAR 檔案中提取任何資源變得非常簡單!
加載 GIF 圖像
假定我們有一個 JAR 檔案,其中包含我們的應用程式要使用的一組 .gif 圖像。下面就是使用 JarResources 通路 JAR 檔案中的圖像檔案的方法:
JarResources jar = new JarResources ("Images.jar");
Image logo =
Toolkit.getDefaultToolkit().createImage (jar.getResource ("logo.gif");
這段代碼說明我們可以建立一個
JarResources
對象,并将其初始化為包含我們要使用的資源的 JAR 檔案 --
Images.jar
。随後我們使用
JarResources
的
getResource()
方法将來自 logo.gif 檔案的原始資料提供給 AWT Toolkit 的
createImage()
方法。
命名說明
JarResource 是一個非常簡單的示例,它說明了如何使用 java 1.1 所提供的各種功能來處理 JAR 和 zip 檔案檔案。
關于命名的簡要說明。Java 中的歸檔支援實際上是以流行的 zip 歸檔格式為起點的(請參閱 "Java Tip 21: Use archive files to speed up applet loading")。是以,在最初實作處理檔案檔案的 java 支援時,所有類檔案以及諸如此類的東西并未放在 java.util.zip 包中;這些類通常以 "
Zip
" 開頭。但在轉向 java 1.1 時,功能已發生了變化,檔案檔案的名稱也更具有 java 特征。是以,現在我們稱之為 JAR 檔案的檔案基本上是 zip 檔案。
工作方式
JarResources
類的重要資料域用來跟蹤和存儲指定 JAR 檔案的内容:
public final class JarResources {
public boolean debugOn=false;
private Hashtable htSizes=new Hashtable();
private Hashtable htJarContents=new Hashtable();
private String jarFileName;
這樣,該類的執行個體化設定 JAR 檔案的名稱,然後轉到
init()
方法完成全部實際工作。
public JarResources(String jarFileName) {
this.jarFileName=jarFileName;
init();
}
現在,
init()
方法隻将指定 JAR 檔案的整個内容加載到一個 hashtable(通過資源名通路)中。
這是一個相當有用的方法,下面我們對它作進一步的分析。
ZipFile
類為我們提供了對 JAR/zip 檔案頭資訊的基本通路方法。這類似于檔案系統中的目錄資訊。下面我們列出
ZipFile
中的所有條目,并用檔案中每個資源的大小添充 htSizes hashtable:
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();
接下來,我們使用
ZipInputStream
類通路檔案。
ZipInputStream
類完成了全部魔術,允許我們單獨讀取檔案中的每個資源。我們從檔案中讀取組成每個資源的精确位元組數,并将其存儲在 htJarContents hashtable 中,您可以通過資源名通路這些資料:
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 表示大小未知。
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;
}
// 添加到内部資源 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();
}
}
請注意,用來辨別每個資源的名稱是檔案中資源的限定路徑名,例如,不是包中的類名 -- 即 java.util.zip 包中的
ZipEntry
類将被命名為 "java/util/zip/ZipEntry",而不是 "java.util.zip.ZipEntry"。
代碼的最後一個重要部分是簡單的測試驅動程式。該測試驅動程式是一個簡單的應用程式,它接收 JAR/zip 檔案名和資源名。它試圖發現檔案中的資源檔案,然後将成功或失敗的消息報告出來:
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+").");
}
}
} // JarResources 類結束。
您已了解了這個類。一個易于使用的類,它隐藏了使用打包在 JAR 檔案中的資源的全部棘手問題。
練習
現在您對從檔案檔案中提取資源已有了一定的認識,下面是可用來修改和擴充
JarResources
類的一些說明:
- 不在構造期間一次性加載全部内容,而要延遲加載。對于大型 JAR 檔案,構造期間可能沒有足夠的記憶體加載全部檔案。
- 不隻是提供類似
這樣的一般讀方法,我們還可提供資源特定的讀方法 -- 例如,用來傳回 javagetResource()
對象的Image
方法,用來傳回 javagetImage()
對象的Class
方法(在自定義的類加載程式的協助下),等等。如果 JAR 檔案足夠小,則我們可以根據它們的擴充名(.gif、.class 等等)預先建構全部資源。getClass()
- 某些方法應該提供關于給定 JAR 檔案本身(基本上是
的包裝)的資訊,包括:Jar/zip 的條目數;傳回全部資源名的 Enumerator;傳回特定條目長度(和其他屬性)的讀方法;允許編制索引的讀方法,這僅僅是舉幾個例子。ZipFile
- 可對
進行擴充,以供 applet 使用。通過利用 applet 參數和JarResources
類,就可以從網絡上下載下傳 JAR 内容,而不是将檔案作為本地檔案打開。此外,我們還可将該類擴充為一個自定義的 java 内容處理程式。URLConnection
小結
如果您曾經渴望知道如何從 JAR 檔案中提取圖像,那麼您現在已學到了一種方法。有了本技巧提供的這個新類,您就不僅可以用 JAR 檔案處理圖像,而且可以将提取魔術用于 JAR 檔案中的任何資源。
作者簡介 Arthur Choi 目前是 IBM 的一位顧問程式員。他曾先後在幾個公司任職,包括 SamSung Network Laboratory 和 MITRE。他參與過的項目包括客戶機/伺服器系統、分布式對象計算和網絡管理。他在各種作業系統環境下用過多種語言。他于 1981 年開始用 FORTRAN IV 和 COBOL 進行程式設計。後來他轉向 C 和 C++,最近兩年他一直在用 Java 工作。他最感興趣的 Java 應用是廣域網中的資料倉庫和網際網路上的并行處理和分布式處理(使用基于代理的程式設計)。Reach Arthur 的電子郵件位址為 [email protected]。 John Mitchell,先後做過雇員、咨詢人員,現在是自己公司的老闆,過去十年他所投資的領域包括前沿計算機軟體的開發、對其他開發人員提供建議和教育訓練。他的咨詢範圍包括 java 技術、編譯器、解釋器、基于 Web 應用和網際網路商務等。John 是 Making Sense of java: A Guide for Managers and the Rest of Us 一書的作者之一,并在程式設計雜志上發表了許多文章。除了為 JavaWorld 撰寫 java Tips 專欄之外,他還主持着 comp.lang.tcl.announce 和 comp.binaries.geos 新聞討論區。Reach John 的電子郵件位址為 [email protected]。 |
參考資源
- 這是類檔案
JarResources.javaJarResources.java
-
JARs
http://www.javasoft.com/products/jdk/1.1/docs/guide/jar/index.html
-
有關 java 的歸檔支援的詳細資訊,請參閱 "Java Tip 21: Use archive files to speed up applet loading"
http://www.javaworld.com/javatips/jw-javatip21.html