2009-08-15 27 views
49

Làm cách nào để tạo tệp JAR theo lập trình sử dụng java.util.jar.JarOutputStream? Tệp JAR do chương trình của tôi tạo ra có vẻ chính xác (nó trích xuất tốt) nhưng khi tôi thử tải một thư viện từ nó, Java phàn nàn rằng nó không thể tìm thấy các tệp được lưu trữ rõ ràng bên trong nó. Nếu tôi trích xuất tệp JAR và sử dụng công cụ dòng lệnh jar của Sun để nén lại thì thư viện kết quả sẽ hoạt động tốt. Tóm lại, có điều gì đó sai với tệp JAR của tôi.Làm thế nào để sử dụng JarOutputStream để tạo một tệp JAR?

Vui lòng giải thích cách tạo tệp JAR theo lập trình, hoàn chỉnh với tệp kê khai.

+2

Có lẽ bạn nên hiển thị hiện tại (không làm việc) của bạn giải pháp – ChssPly76

Trả lời

82

Nó chỉ ra rằng JarOutputStream có ba tật không có giấy tờ:

  1. tên thư mục phải kết thúc với dấu gạch chéo '/'.
  2. Đường dẫn phải sử dụng dấu gạch chéo '/', không phải '\'
  3. Mục nhập có thể không bắt đầu bằng dấu gạch chéo '/'.

Dưới đây là cách chính xác để tạo ra một 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(); 
    } 
} 
+14

những 'quirks' này thực sự là một phần của đặc tả zip (tệp jar chỉ là các tệp zip có tệp kê khai và phần mở rộng khác). Tôi đồng ý rằng nó nên được ghi lại trong tài liệu API, mặc dù - Tôi khuyên bạn nên mở một vấn đề (http://bugs.sun.com/bugdatabase/) –

+5

Quan trọng hơn, API sẽ ngăn bạn tạo tệp ZIP/JAR không hợp lệ theo ném ngoại lệ nếu bạn nhập sai loại dấu gạch chéo hoặc tự động chuyển đổi chúng. Đối với các thư mục kết thúc bằng dấu gạch chéo, nó chắc chắn phải được ghi lại vì không có cách nào để sửa nó tự động. Tôi đã gửi báo cáo lỗi nhưng chưa được chấp nhận. – Gili

+0

Mặt trời cổ điển/Oracle. Đã đóng dưới dạng "không phải lỗi": http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6873352 – Gili

3

Dưới đây là một số mẫu mã để tạo một tập tin JAR sử dụng JarOutputStream:

+1

Tôi đã làm điều này. Trong thực tế, ví dụ bạn tham chiếu đến không chỉ ra rằng người ta phải đặt rõ ràngNextEntry() trên các tên thư mục hoặc gọi JarOutputStream.closeEntry(). Cái gì khác phải sai. – Gili

+0

Ah, OK. Đó là một chút khó khăn để cung cấp một giải pháp tốt hơn mà không nhìn thấy bất kỳ mã nào, vì vậy tôi chỉ chỉ cho bạn tại tham chiếu đó. Rất vui vì bạn đã tìm ra nó. – ars

+0

Tôi đánh giá cao sự giúp đỡ của bạn. Cảm ơn bạn! – Gili

9

Có một "đứa" chú ý: tên Tất cả của JarEntry không nên bắt đầu bằng "/".

Ví dụ: Tên mục nhập jar cho tệp kê khai là "META-INF/MANIFEST.MF" và không phải là "/META-INF/MANIFEST.MF".

Quy tắc tương tự phải được tuân thủ cho tất cả các mục nhập jar.

1

Bạn có thể làm điều đó với mã này:

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 biến: đường dẫn đến tập tin JAR

FILE biến: tên và định dạng

+0

Điều này làm gì thêm câu trả lời được chấp nhận không nói? – Gili

+0

Câu trả lời được chấp nhận sử dụng nhiều mã hơn của tôi. Tôi nghĩ, bạn không muốn viết nhiều mã khi bạn có thể viết một chút. – Muskovets

+1

Trong tất cả sự công bằng, câu trả lời của bạn chứa ít mã hơn vì nó ít hơn. Câu trả lời được chấp nhận có chứa mã làm mát đầu vào theo định dạng được mong đợi bởi JarOutputStream. Mã của bạn sẽ không thành công nếu bạn chạy dưới Windows hoặc nếu tên thư mục không kết thúc bằng dấu gạch chéo. – Gili

Các vấn đề liên quan