如何使用JarOutputStream创建JAR文件?

时间:2009-08-15 05:23:14

标签: java jar

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

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

6 个答案:

答案 0 :(得分:91)

事实证明JarOutputStream有三个未记载的怪癖:

  1. 目录名称必须以'/'斜杠结尾。
  2. 路径必须使用'/'斜杠,而不是'\'
  3. 条目不得以'/'斜杠开头。
  4. 以下是创建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();
      }
    }
    

答案 1 :(得分:10)

还有另一个“怪癖”要注意:所有JarEntry的名字都不应以“/".

开头

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

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

答案 2 :(得分:2)

以下是使用JarOutputStream创建JAR文件的示例代码:

答案 3 :(得分: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变量:名称和格式

答案 4 :(得分:0)

此答案将解决相对路径问题。

private static void createJar(File source, JarOutputStream target) {
        createJar(source, source, target);
    }

    private static void createJar(File source, File baseDir, JarOutputStream target) {
        BufferedInputStream in = null;

        try {
            if (!source.exists()){
                throw new IOException("Source directory is empty");
            }
            if (source.isDirectory()) {
                // For Jar entries, all path separates should be '/'(OS independent)
                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()) {
                    createJar(nestedFile, baseDir, target);
                }
                return;
            }

            String entryName = baseDir.toPath().relativize(source.toPath()).toFile().getPath().replace("\\", "/");
            JarEntry entry = new JarEntry(entryName);
            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();
        } catch (Exception ignored) {

        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (Exception ignored) {
                    throw new RuntimeException(ignored);
                }
            }
        }
    }

答案 5 :(得分:0)

好,因此,根据要求,这是Gili的代码,已修改为使用相对路径而不是绝对路径。 (将“ inputDirectory”替换为您选择的目录。)我刚刚对其进行了测试,但是如果它不起作用,我就知道了。

   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);
      File inputDirectory = new File("inputDirectory");
      for (File nestedFile : inputDirectory.listFiles())
         add("", nestedFile, target);
      target.close();
   }

   private void add(String parents, File source, JarOutputStream target) throws IOException
   {
      BufferedInputStream in = null;
      try
      {
         String name = (parents + source.getName()).replace("\\", "/");

         if (source.isDirectory())
         {
            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(name, nestedFile, target);
            return;
         }

         JarEntry entry = new JarEntry(name);
         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();
      }
   }