2014-10-02 136 views
1

我正在替换或更新jar文件中某个目录内的某些文件。如何替换jar文件中某个目录内的某些文件?

我已阅读过几篇文章。在这个环节Updating .JAR's contents from code

正在非常有帮助,我听不懂的作用和使用ZipInputStream,ZipOutputStream和的ZipEntry的,等的代码(JarUpdater类)给出..

然而,当我运行它,

  1. 我有一个EOF异常

[由MK7编辑:我发现了jar文件已损坏我通过它去了20次左右之后。所以在我用新的jar文件替换之后,EOF Exception就消失了。下面的其他两个问题仍然没有解决]

  1. 这两个新的xml文件只被复制到jar文件的“根目录”。

  2. 这两个新的xml文件从不替换名为/ conf的目录内的两个原始文件。

为了用新的替换xml文件,我应该更改哪行代码?

通过System.out.println,我确实看到while循环遍历每个目录并按照预期在每个文件上进行比较。一个新的临时罐子也是按照预期创建的...... 我认为声明“notInFiles = false”会照顾我的需要,但它不是。

如何进入/ conf并仅替换这两个文件,而不在jar文件的根目录下留下副本?

我错过了什么?感谢您的任何见解!

下面是该链接的代码。

import java.io.*; 
import java.util.zip.ZipEntry; 
import java.util.zip.ZipInputStream; 
import java.util.zip.ZipOutputStream; 


public class JarUpdater { 
public static void main(String[] args) { 

    File[] contents = {new File("abc.xml"), 
      new File("def.xml")}; 

    File jarFile = new File("xyz.jar"); 

    try { 
     updateZipFile(jarFile, contents); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 

} 

public static void updateZipFile(File jarFile, 
           File[] contents) throws IOException { 
    // get a temp file 
    File tempFile = File.createTempFile(jarFile.getName(), null); 
    // delete it, otherwise you cannot rename your existing zip to it. 
    tempFile.delete(); 
    System.out.println("tempFile is " + tempFile); 
    boolean renameOk=jarFile.renameTo(tempFile); 
    if (!renameOk) 
    { 
     throw new RuntimeException("could not rename the file  "+jarFile.getAbsolutePath()+" to "+tempFile.getAbsolutePath()); 
    } 
    byte[] buf = new byte[1024]; 

    ZipInputStream zin = new ZipInputStream(new FileInputStream(tempFile)); 
    ZipOutputStream out = new ZipOutputStream(new FileOutputStream(jarFile)); 

    ZipEntry entry = zin.getNextEntry(); 
    while (entry != null) { 
     String name = entry.getName(); 
     boolean notInFiles = true; 
     for (File f : contents) { 
      System.out.println("f is " + f); 
      if (f.getName().equals(name)) { 
       // that file is already inside the jar file 
       notInFiles = false; 
       System.out.println("file already inside the jar file"); 
       break; 
      } 
     } 
     if (notInFiles) { 
      System.out.println("name is " + name); 
      System.out.println("entry is " + entry); 
      // Add ZIP entry to output stream. 
      out.putNextEntry(new ZipEntry(name)); 
      // Transfer bytes from the ZIP file to the output file 
      int len; 
      while ((len = zin.read(buf)) > 0) { 
       out.write(buf, 0, len); 
      } 
     } 
     entry = zin.getNextEntry(); 
    } 
    // Close the streams 
    zin.close(); 
    // Compress the contents 
    for (int i = 0; i < contents.length; i++) { 
     InputStream in = new FileInputStream(contents[i]); 
     // Add ZIP entry to output stream. 
     out.putNextEntry(new ZipEntry(contents[i].getName())); 
     // Transfer bytes from the file to the ZIP file 
     int len; 
     while ((len = in.read(buf)) > 0) { 
      out.write(buf, 0, len); 
     } 
     // Complete the entry 
     out.closeEntry(); 
     in.close(); 
    } 
    // Complete the ZIP file 
    out.close(); 
    tempFile.delete(); 
} 
} 

回答

2

在你的第一个周期(while循环),您复制您不希望替换你不输出zip文件关闭项的条目。添加out.closeEntry();这样的:

// Add ZIP entry to output stream. 
out.putNextEntry(new ZipEntry(name)); 
// Transfer bytes from the ZIP file to the output file 
int len; 
while ((len = zin.read(buf)) > 0) { 
    out.write(buf, 0, len); 
} 
// ADD THIS LINE: 
out.closeEntry(); 

此外,当您检查条目是否是要被替换,你应该把它比作一个完整路径,而不仅仅是文件的名称。例如,如果要替换/conf文件夹中的abc.xml,则应将条目名称与"/conf/abc.xml"进行比较,而不要将其与"abc.xml"进行比较。

要正确检查条目是否是要被替换:

String name = entry.getName(); 
boolean notInFiles = true; 
for (File f : contents) { 
    System.out.println("f is " + f); 
    if (name.equals("/conf/" + f.getName()) { 
     // that file is already inside the jar file 
     notInFiles = false; 
     System.out.println("file already inside the jar file"); 
     break; 
    } 
} 

当您添加条目,其输出是被替换的文件,你还必须指定其全路径条目名称,例如"/conf/abc.xml"而不仅仅是"abc.xml",因为它会将"abc.xml"放在输出zip的根目录中。

要做到这一点,与"/conf/"这样启动项名称:

// Add ZIP entry to output stream. 
out.putNextEntry(new ZipEntry("/conf/" + contents[i].getName())); 
+0

我只是仔细检查。代码是否有out.closeEntry();和in.close(); – mk7 2014-10-02 07:35:11

+0

这是jar文件被损坏。所以在我替换了jar文件之后,EOF异常消失了。我编辑了我的原始问题,并删除了我第一次遇到的EOF异常问题。但即使是现在,我仍然没有取代这两个文件。 – mk7 2014-10-02 07:37:04

+0

添加代码以正确检查条目是否要被替换(您没有正确检查,因此您没有过滤掉您想要替换的条目)。 – icza 2014-10-02 07:39:45

1

对于协议jar:file:(可用于所有的zip文件)的URI,您可以使用zip文件系统。导出URI

URI jarUri = new URI("jar:" + jarFile.toURI().toString()); // "jar:file:/C:/../xyz.jar" 
Map<String, String> zipProperties = new HashMap<>(); 
zipProperties.put("encoding", "UTF-8"); 
try (FileSystem zipFS = FileSystems.newFileSystem(jarUri, zipProperties)) { 
    for (File file : contents) { 
     Path updatePath = zipFS.getPath("/" + file.getName()); 
     Files.delete(updatePath); 
     Files.copy(file.toPath(), updatePath, StandardCopyOption.REPLACE_EXISTING); 
    } 
} // closes. 

一种方法是前缀“罐子”到File.toURI()

这是一个更优雅和抽象,并允许Files.copy进出zip。有些东西要留在工具箱里。

+0

这很好。我见过一些关于使用File.toURI()的文章。我不完全理解如何以及为什么它与这种方法一起工作。我可以准备好完整的代码,以便我可以从头到尾看到吗? thx多! – mk7 2014-10-02 08:00:15

+0

完成后,'File.toURI()'prepends“file:/”,将Windows'\'变成'/'并将空格替换为'%20'。对于任何zip格式,Java为'jar:'提供了一个内置协议处理程序。 – 2014-10-02 08:07:46

相关问题