2009-08-15 71 views
49

如何使用java.util.jar.JarOutputStream以编程方式创建JAR文件?我的程序生成的JAR文件看起来是正确的(它提取的很好),但是当我尝试从它加载一个库时,Java抱怨它找不到明确存储在其中的文件。如果我解压缩JAR文件并使用Sun的jar命令行工具重新压缩它,则生成的库工作正常。总之,我的JAR文件出了问题。如何使用JarOutputStream创建JAR文件?

请解释如何以编程方式创建JAR文件,并附带清单文件。

+2

也许你应该表现出你目前(非工作)解决方案 – ChssPly76 2009-08-15 06:14:35

回答

82

事实证明,JarOutputStream有三个无证的怪癖:

  1. 目录名称必须结束用'/'斜杠。
  2. 路径必须使用'/'斜线,而不是'\'
  3. 项目不能以'/'斜杠开头。

下面是创建一个JAR文件的正确方法:

public void run() throws IOException 
{ 
    Manifest manifest = new Manifest(); 
    manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); 
    JarOutputStream target = new JarOutputStream(new FileOutputStream("output.jar"), manifest); 
    add(new File("inputDirectory"), target); 
    target.close(); 
} 

private void add(File source, JarOutputStream target) throws IOException 
{ 
    BufferedInputStream in = null; 
    try 
    { 
    if (source.isDirectory()) 
    { 
     String name = source.getPath().replace("\\", "/"); 
     if (!name.isEmpty()) 
     { 
     if (!name.endsWith("/")) 
      name += "/"; 
     JarEntry entry = new JarEntry(name); 
     entry.setTime(source.lastModified()); 
     target.putNextEntry(entry); 
     target.closeEntry(); 
     } 
     for (File nestedFile: source.listFiles()) 
     add(nestedFile, target); 
     return; 
    } 

    JarEntry entry = new JarEntry(source.getPath().replace("\\", "/")); 
    entry.setTime(source.lastModified()); 
    target.putNextEntry(entry); 
    in = new BufferedInputStream(new FileInputStream(source)); 

    byte[] buffer = new byte[1024]; 
    while (true) 
    { 
     int count = in.read(buffer); 
     if (count == -1) 
     break; 
     target.write(buffer, 0, count); 
    } 
    target.closeEntry(); 
    } 
    finally 
    { 
    if (in != null) 
     in.close(); 
    } 
} 
+14

这些'怪癖'实际上是zip规范的一部分(jar文件只是带有清单和不同扩展名的zip文件)。我同意它应该记录在API文档中,但是 - 我建议打开一个问题(http://bugs.sun.com/bugdatabase/) – 2009-08-15 19:18:03

+5

更重要的是,API应该可以防止你通过创建无效的ZIP/JAR文件如果您传递了错误类型的斜杠或自动转换它们,则抛出异常。对于以斜线结尾的目录,由于无法自动纠正,所以应该记录它。我提交了一个错误报告,但尚未被接受。 – Gili 2009-08-16 15:52:38

+0

经典的Sun/Oracle。关闭为“不是bug”:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6873352 – Gili 2012-01-17 18:06:13

3

下面是一些示例代码使用的JarOutputStream创建一个JAR文件:

+1

我已经在做这个。实际上,您引用的示例未能指出必须在目录名称上明确放置nextEntry()或调用JarOutputStream.closeEntry()。其他的一定是错的。 – Gili 2009-08-15 05:39:43

+0

啊,好的。在没有看到任何代码的情况下提供更好的解决方案有点困难,所以我只是指出你的参考。很高兴你知道了。 – ars 2009-08-15 07:53:20

+0

我感谢你的帮助。谢谢! – Gili 2009-08-15 17:15:57

9

还有一个“怪癖”注意:所有的JarEntry的名字不应该与“/”开头。

例如:清单文件的jar条目名称是“META-INF/MANIFEST.MF”,而不是“/META-INF/MANIFEST.MF”。

所有jar条目都应遵循相同的规则。

1

您可以使用此代码做到这一点:

public void write(File[] files, String comment) throws IOException { 
    FileOutputStream fos = new FileOutputStream(PATH + FILE); 
    JarOutputStream jos = new JarOutputStream(fos, manifest); 
    BufferedOutputStream bos = new BufferedOutputStream(jos); 
    jos.setComment(comment); 
    for (File f : files) { 
     print("Writing file: " + f.toString()); 
     BufferedReader br = new BufferedReader(new FileReader(f)); 
     jos.putNextEntry(new JarEntry(f.getName())); 
     int c; 
     while ((c = br.read()) != -1) { 
      bos.write(c); 
     } 
     br.close(); 
     bos.flush(); 
    } 
    bos.close(); 
// JarOutputStream jor = new JarOutputStream(new FileOutputStream(PATH + FILE), manifest); 

} 

PATH变量:路径JAR文件

FILE变量:名称和格式

+0

这是什么增加,接受的答案还没有说? – Gili 2016-10-13 12:36:44

+0

接受的答案使用比我更多的代码。我认为,你可以写一点时不想编写太多的代码。 – Muskovets 2017-12-10 18:06:04

+1

在所有适当的公平性中,您的答案包含的代码较少,因为它的确较少。接受的答案包含按照JarOutputStream预期的格式输入的代码。如果您在Windows下运行,或者目录名称不以斜线结尾,您的代码将无提示失败。 – Gili 2017-12-12 02:03:41