从*.jar文件或输入流中获取JarFile并写入文件?

4

我有一个jar或war文件。

我正在以编程方式读取此jar文件,当我在该jar文件中找到另一个jar文件时,我想再次以编程方式读取它。

但是JarFile仅提供getInputStream,我无法将其传递给JarFile(File file)构造函数。

如何从jar文件中读取jar文件?

编辑:我考虑从类加载器或其他方式获取文件。


虽然没有什么能阻止你这么做,但是jar文件中不应包含其他的jar文件。我建议你另寻它路来实现你想要达成的功能。http://java.sun.com/j2se/1.5.0/docs/guide/jar/jar.html - Nick Holt
@Nick Eclipse插件是一个反例,它们作为jar文件交付,并经常包含嵌套的jar文件。 - Rich Seller
@Nick Holt:在我看来,如果你被允许在你的JAR中分发其他JAR文件,那么你应该这样做。我认为这样可以更轻松地管理依赖关系,因为你可以将你的JAR文件交给别人,他们就拥有了一个包含所有内容的文件。 - Thomas Owens
4个回答

7

更新:很抱歉这可能对您来说太晚了,但我在评论中看到了您的最后一个问题。因此,我修改了示例,以显示每个嵌套条目直接复制到OutputStream而无需膨胀外部jar。

在这种情况下,OutputStreamSystem.out,但它可以是任何OutputStream(例如,写入文件...)。


不需要使用临时文件。您可以使用JarInputStream而不是JarFile,将外部条目的InputStream传递给构造函数,然后可以读取jar的内容。

例如:

JarFile jarFile = new JarFile(warFile);

Enumeration entries = jarFile.entries();

while (entries.hasMoreElements()) {
    JarEntry jarEntry = (JarEntry) entries.nextElement();

    if (jarEntry.getName().endsWith(".jar")) {
        JarInputStream jarIS = new JarInputStream(jarFile
                .getInputStream(jarEntry));

        // iterate the entries, copying the contents of each nested file 
        // to the OutputStream
        JarEntry innerEntry = jarIS.getNextJarEntry();

        OutputStream out = System.out;

        while (innerEntry != null) {
            copyStream(jarIS, out, innerEntry);
            innerEntry = jarIS.getNextJarEntry();
        }
    }
}

...

/**
 * Read all the bytes for the current entry from the input to the output.
 */
private void copyStream(InputStream in, OutputStream out, JarEntry entry)
        throws IOException {
    byte[] buffer = new byte[1024 * 4];
    long count = 0;
    int n = 0;
    long size = entry.getSize();
    while (-1 != (n = in.read(buffer)) && count < size) {
        out.write(buffer, 0, n);
        count += n;
    }
}

我认为您无法将JarEntry处理为ZipFile并获取entries()并进行操作。 - blefesd
试一下,它能工作。一个jar文件只是一个带有不同扩展名和约定的zip文件,并且它有一个清单。 - Rich Seller
但是从JarEntry或ZipEntry中,您无法获取包含在JAR中的所有条目。像这样:JarFile jf = new JarFile(jarName); 枚举<JarEntry> en = jf.entries(); 我需要递归地遍历这些JAR文件并在其中搜索。从JarEntry中我无法获取entries()。 或者至少我还没有找到如何获取。 - blefesd
我尝试了这段代码。innerEntry的大小为-1。因此,我无法将正确数量的字节写入输出流中!? - Michael K.
我必须更正我的评论。innerEntry 是有效大小的。只有我的 Eclipse 调试器显示为 -1。 - Michael K.
显示剩余3条评论

1

你可以在文件系统中创建JAR文件,类似于以下内容:

 File tempFile=TempFile.createFile("newJar",".jar");

将Stream写入其中。然后,您可以构建JarFile(tempFile)并处理它...

如果程序作为未签名的applet/JNLP运行,则忘记在文件系统中创建文件的权限。


是的,这个方法可以运行,但我不确定将临时文件写在某个地方是否是最佳选择。如果没有其他解决方案而不需要创建临时文件,那就更好了。 - blefesd
1
不知道;文件必须存在于文件系统中(我不相信有像InMemoryFile这样的东西:)他们故意这样做,你不能实例化JarFile而不传递文件。 - ante.sabo

0
通常情况下,问题在于如何获取InputStream并进行处理。这样可以使您抽象出大多数“Web服务器上的jar中的jar”等问题。
显然,在这种情况下,使用JarFile.getInputStream()和JarInputStream()方法。

-1

递归使用JarFile再次读取新的jar文件。例如,

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;


public class JarReader {

public static void readJar(String jarName) throws IOException {
    String dir = new File(jarName).getParent();
    JarFile jf = new JarFile(jarName);
    Enumeration<JarEntry> en = jf.entries();
    while(en.hasMoreElements()) { 
        ZipEntry ze = (ZipEntry)en.nextElement();
        if(ze.getName().endsWith(".jar")) { 
            readJar(dir + System.getProperty("file.separator") + ze.getName());
        } else {
            InputStream is = jf.getInputStream(ze);
            // ... read from input stream
            is.close();
            System.out.println("Processed class: " + ze.getName());
        }   
    }
}

public static void main(String[] args) throws IOException {
    readJar(args[0]);
}

}

1
这段代码无法正常工作:JarFile jf = new JarFile(jarName); JarFile期望在系统上找到jar文件的名称(文件)。否则你会得到java.util.zip.ZipException: 找不到指定路径。 - blefesd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接