2009-08-15 5 views
49

Как создать файл JAR программно с помощью java.util.jar.JarOutputStream? Файл JAR, созданный моей программой, выглядит корректно (он отлично извлечения), но когда я пытаюсь загрузить из него библиотеку, Java жалуется, что не может найти файлы, которые явно хранятся внутри него. Если я извлечу JAR-файл и воспользуюсь инструментом командной строки Sun jar, чтобы повторно сжать его, результирующая библиотека отлично работает. Короче говоря, что-то не так с моим файлом JAR.Как использовать JarOutputStream для создания JAR-файла?

Просьба пояснить, как создать файл JAR программно, в комплекте с файлом манифеста.

+2

Может быть, вы должны показать текущий (нерабочий) Решение – ChssPly76

ответ

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/) –

+5

. Более важно, что API должен помешать вам создавать недопустимые файлы ZIP/JAR с помощью исключая исключения, если вы передаете неправильный тип косой черты или автоматически конвертируете их. Что касается каталогов, заканчивающихся косой чертой, это должно быть обязательно задокументировано, так как нет возможности исправить это автоматически. Я подал сообщение об ошибке, но он еще не принят. – Gili

+0

Классический Sun/Oracle. Закрыто как «не ошибка»: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6873352 – Gili

3

Вот некоторые примеры кода для создания файла JAR с помощью JarOutputStream:

+1

Я уже делаю это. Фактически, в примере, на котором вы ссылались, не указывается, что нужно явно putNextEntry() в именах каталогов или вызвать JarOutputStream.closeEntry(). Что-то еще должно быть неправильно. – Gili

+0

А, ОК. Было немного сложно предложить лучшее решение, не видя никакого кода, поэтому я просто указал вам на эту ссылку. Рад, что ты это понял. – ars

+0

Я ценю вашу помощь. Спасибо! – Gili

9

Там другая «причуда» обратить внимание: все имена JarEntry не должны начинаться с «/».

Например: Имя записи в банке для файла манифеста «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

+0

Принятый ответ использует больше кода, чем мой. Я думаю, вы не хотите писать очень много кода, когда можете написать немного. – Muskovets

+1

Во всей справедливости ваш ответ содержит меньше кода, потому что он делает меньше. В принятом ответе содержится код, который массирует вход в формат, ожидаемый JarOutputStream. Ваш код будет терпеть неудачу, если вы запустите под Windows или если имена каталогов не заканчиваются косой чертой. – Gili

Смежные вопросы