2009-08-15 23 views
49

java.util.jar.JarOutputStreamを使用してプログラムでJARファイルを作成するにはどうすればよいですか?私のプログラムで生成されたJARファイルは正しく見えますが、そこからライブラリをロードしようとすると、内部にはっきりと格納されているファイルを見つけることができません。 JARファイルを解凍し、Sunのjarコマンドラインツールを使用して再圧縮すると、結果のライブラリは正常に動作します。つまり、JARファイルに何か問題があります。JarOutputStreamを使用してJARファイルを作成する方法は?

JARファイルをプログラムで作成し、マニフェストファイルで完成させる方法を説明してください。

+2

おそらく、あなたがあなたの現在の(非稼働)を示すべきです解決策 – ChssPly76

答えて

82

それはJarOutputStreamは3人の文書化されていない癖を持っていることが判明:

  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

これらの 'quirks'は実際には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

ここJarOutputStreamを使用してJARファイルを作成するためのいくつかのサンプルコードです:

+1

私はすでにこれをやっています。実際には、参照した例では、ディレクトリ名に明示的にNextEntry()を挿入するか、JarOutputStream.closeEntry()を呼び出さなければならないことを指摘していません。他の何かが間違っている必要があります。 – Gili

+0

ああ、OK。コードを見ることなく、より良いソリューションを提供するのは少し難しかったので、私はちょうどその参照を指摘しました。あなたがそれを考え出してうれしい。 – ars

+0

私はあなたの助けに感謝します。ありがとうございました! – Gili

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

+0

受け入れられた答えは私よりも多くのコードを使用しています。私は、あなたが少し書くことができるときには非常に多くのコードを書いたくないと思います。 – Muskovets

+1

すべて公平であるため、あなたの答えには少ないコードしか含まれていません。受け入れられた答えには、入力をJarOutputStreamが期待する形式にマッサージするコードが含まれています。 Windowsで実行する場合、またはディレクトリ名がスラッシュで終わっていない場合、コードは自動的に失敗します。 – Gili

関連する問題