Skip to content

从 Java 档案 (JAR) 中读取文件 #6

@jamesfancy

Description

@jamesfancy

原文链接已经失效,所以删去了
2003年6月发表于 计算机世界网
2004年3月发于 CCW-I
2006年9月搬至 CSDN

Java 档案 (Java Archive, JAR) 文件是基于 Java 技术的打包方案。它们允许开发人员把所有相关的内容 (.class、图片、声音和支持文件等) 打包到一个单一的文件中。JAR 文件格式支持压缩、身份验证和版本,以及许多其它特性。

从 JAR 文件中得到它所包含的文件内容是件棘手的事情,但也不是不可以做到。这篇技巧就将告诉你如何从 JAR 文件中取得一个文件。我们会先取得这个 JAR 文件中的文件目录,然后读取指定的文件。

如果你熟悉常见的 ZIP 格式,你会发现 JAR 文件和它区别不大。JAR 文件提供了一个把多个文件打包到一个文件中的方法,而且被打包的每个文件都可以分别压缩。JAR 文件可以添加一个被称为 manifest 的东西,它们允许开发人员添加与内容有关的其它信息。例如,manifest 可以指明由 JAR 文件中的哪一个文件开始运行应用程序,或者指定这个库的版本等。

Java 2 SDK 标准版提供了一个 jar 工具,你可以通过它在控制台下读写 JAR 文件。然后,也许有些时候你想在你的程序中读写 JAR 文件。(这篇技巧仅涉及了在程序中读 JAR 文件的内容。)非常高兴,你可以做到,并且不需要考虑解压的问题,因为类库已经帮你处理了。你要用到的类都在 java.util.jar 包中。这里要用到的主要的类是 JarFile 类,它是一个 .jar 文件自身的引用。其中的每个文件则由 JarEntry 引用。

现在开始,传递一个参数给 JarFile 的构造函数创建一个 JarFile 实例,这个参数可能是 String 也可以是 File,它是一个 .jar 文件的位置:

JarFile jarFile = new JarFile("thefile.jar");

或者

File file = new File("thefile.jar");
JarFile jarFile = new JarFile(file);

它还有其它一些构造函数,支持身份验证和标记文件为删除。不过这里不会涉及到这些构造函数。

在你得到一个 JAR 文件的引用之后,你就可以读了其内容的目录了。JarFileentries 方法返回一个所有条目的 Enumeration 对象,你还可以从 manifest 文件中获得它的属性、身份验证信息以及其它的信息,如条目的名称和大小。

// 译者注:enum 在 Java 5.0 中是关键字,所以该例在 5.0 中应该编译失败
// 但英文原著发表于 Java 5.0 出现之前,所以可以使用 enum 作变量名
// 为忠于原著,这里未作修改
Enumeration enum = jarFile.entries();
while (enum.hasMoreElements()) {
    process(enum.nextElement());
}

以前提到过,每个个体都是一个 JarEntry。这个类有一些诸如 getNamegetSizegetCompressedSize 的方法。

让我们举例说明如何在程序中使用这些特性。下面的程序显示 JAR 文件的内容列表及各项的名称、大小和压缩后的大小。(这很类似于使用带 "t" 和 "v" 参数的 jar 命令。)

import java.io.*;
import java.util.*;
import java.util.jar.*;

public class JarDir {
    public static void main (String args[]) 
        throws IOException {
        if (args.length != 1) {
            System.out.println("Please provide a JAR filename");
            System.exit(-1);
        }
        JarFile jarFile = new JarFile(args[0]);
        Enumeration enum = jarFile.entries();
        while (enum.hasMoreElements()) {
            process(enum.nextElement());
        }
    }

    private static void process(Object obj) {
        JarEntry entry = (JarEntry)obj;
        String name = entry.getName();
        long size = entry.getSize();
        long compressedSize = entry.getCompressedSize();
        System.out.println(name + " " + size + " " + compressedSize);
    }
}

如果你用 J2SE 1.4.1 中的 jce.jar 来试验上述的 JarDir 程序,你应该看像下面那样的输出 (在 ... 处应该显示更多文件):

META-INF/MANIFEST.MF    5315    1910
META-INF/4JCEJARS.SF    5368    1958
META-INF/4JCEJARS.DSA   2207    1503
META-INF/       0       2
javax/  0       0
javax/crypto/   0       0
javax/crypto/interfaces/        0       0
javax/crypto/interfaces/DHKey.class     209     185
javax/crypto/interfaces/DHPublicKey.class       265     215
javax/crypto/interfaces/DHPrivateKey.class      267     215
javax/crypto/interfaces/PBEKey.class    268     224
javax/crypto/SecretKey.class    167     155
...

注意输入内容顶部包含 META-INF 的那几行,这是 menifest 和安全验证信息。其中大小为 0 的项不是文件,而是目录。

要真正从 JAR 文件中读取文件内容,你必须获得相应条目的 `InputStream`。这不同于 `JarEntry`。`JarEntry` 仅包括了入口信息,却并未包含实际的内容。这很像 `File` 和 `FileInputSteram` 的区别。只访问 `File`,永远不会打开相应的文件,它只读取在目录中的信息。下面告诉你如何从一个条目得到 `InputStream`:

```java
InputStream input = jarFile.getInputStream(entry);

得到输入流之后,你只需要像读其它流一样读它就行了。如果是一个文本流,要记得使用一个 Reader 来从流中获取字符。而对于字节流,如图片,则只好直接读取。

下面的程序演示了从一个 JAR 文件中读取内容。运行程序时,需要指定要从 JAR 文件中读取的文件名,这个文件必须是文本文件类型。

import java.io.*;
import java.util.jar.*;

public class JarRead {
    public static void main (String args[]) 
        throws IOException {
        if (args.length != 2) {
            System.out.println("Please provide a JAR filename and file to read");
            System.exit(-1);
        }
        JarFile jarFile = new JarFile(args[0]);
        JarEntry entry = jarFile.getJarEntry(args[1]);
        InputStream input = jarFile.getInputStream(entry);
        process(input);
        jarFile.close();
    }

    private static void process(InputStream input) 
        throws IOException {
        InputStreamReader isr = 
            new InputStreamReader(input);
        BufferedReader reader = new BufferedReader(isr);
        String line;
        while ((line = reader.readLine()) != null) {
            System.out.println(line);
        }
        reader.close();
    }
}

假设你有一个名为 myfiles.jar 的 JAR 文件,其中有一个名为 spider.txt 文本文件,再假设那个 pider.txt 包含如下文本:

The itsy bitsy spider 
Ran up the water spout
Down came the rain and
Washed the spider out 

运行下面的命令以显示 JAR 文件中该文本文件的内容:

java JarRead myfiles.jar spider.txt

关于 JAR 文件更多的内容,请参阅 JAR file specification

原文的“JAR file specification”是链接到 SUN 的,已更新链接到 Oracle 相关页面

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions