如何替换jar文件中某个目录中的某些文件?

时间:2014-10-02 07:11:11

标签: java jar zipinputstream zipoutputstream

我遇到了替换或更新jar文件中某个目录中某些文件的问题。

我已经阅读了一些帖子。此链接Updating .JAR's contents from code

中给出的代码(JarUpdater类)

对于理解ZipInputStream,ZipOutputStream和ZipEntry等的角色和用法非常有帮助。

但是,当我运行它时,

  1. 我有EOF异常
  2. [由mk7编辑:我发现jar文件在我经历了20次左右后被破坏了。因此,在用新文件替换jar文件后,EOF异常消失了。下面的其他两个问题仍然没有解决]

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

    2. 这两个新的xml文件永远不会替换名为/ conf的目录中的两个原始文件。

    3. 为了用新的xml文件替换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 个答案:

答案 0 :(得分: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();

此外,当您检查是否要替换条目时,应将其与完整路径进行比较,而不仅仅是文件名。例如,如果要替换abc.xml文件夹中的/conf,则应将条目名称与"/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()));

答案 1 :(得分:1)

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

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.

派生URI的一种方法是在前缀&#34; jar:&#34;到File.toURI()

这更优雅和抽象,并且允许Files.copy进出zip。保留在工具柜中的东西。