Java 如何使用 JarOutputStream 创建 JAR 文件?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1281229/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 07:45:33  来源:igfitidea点击:

How to use JarOutputStream to create a JAR file?

javajar

提问by Gili

How does one create a JAR file programmatically using java.util.jar.JarOutputStream? The JAR file produced by my program looks correct (it extracts fine) but when I try loading a library from it Java complains that it cannot find files which are clearly stored inside it. If I extract the JAR file and use Sun's jarcommand-line tool to re-compress it the resulting library works fine. In short, something is wrong with my JAR file.

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

Please explain how to create a JAR file programmatically, complete with a manifest file.

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

采纳答案by Gili

It turns out that JarOutputStreamhas three undocumented quirks:

事实证明,它JarOutputStream具有三个未记录的怪癖:

  1. Directory names must end with a '/' slash.
  2. Paths must use '/' slashes, not '\'
  3. Entries may not begin with a '/' slash.
  1. 目录名称必须以“/”斜线结尾。
  2. 路径必须使用“/”斜杠,而不是“\”
  3. 条目不能以“/”斜线开头。

Here is the correct way to create a Jar file:

以下是创建 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();
  }
}

回答by ars

Here's some sample code for creating a JAR file using the JarOutputStream:

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

回答by Hélio Luiz Alves Rodrigues

There's another "quirk" to pay attention: All JarEntry's names should NOT begin with "/".

还有一个“怪癖”需要注意:所有 JarEntry 的名称都不应该以“/”开头。

For example: The jar entry name for the manifest file is "META-INF/MANIFEST.MF" and not "/META-INF/MANIFEST.MF".

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

The same rule should be followed for all jar entries.

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

回答by Muskovets

You can do it with this code:

你可以用这个代码做到这一点:

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);

}

PATHvariable: path to JAR file

PATH变量:JAR 文件的路径

FILEvariable: name and format

FILE变量:名称和格式

回答by Shehan Dhaleesha

This answer would solve the relative path problem.

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

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);
                }
            }
        }
    }

回答by Erhannis

Ok, so by request, here's Gili's code, modified to use relative paths rather than absolute ones. (Replace "inputDirectory" with the directory of your choice.) I just tested it, but if it doesn't work, lemme know.

好的,所以根据要求,这里是 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();
      }
   }