1

I'm having the problem of replacing or updating some files within a certain directory inside a jar file.

I've read a few post already. The code (the JarUpdater Class) given at this link Updating .JAR's contents from code

is being very helpful for me to understand the role and the use of ZipInputStream, ZipOutputStream and ZipEntry, etc..

However, when I run it,

  1. I have an EOF Exception

[EDITED by mk7: I found out the jar file was corrupted after I went through it 20 times or so. So after I replaced the jar file with a new one, the EOF Exception went away. The other two problems below still remains unsolved]

  1. these two new xml files only get copied to the "root directory" of the jar file.

  2. these two new xml files NEVER replaced the two original files inside a directory called /conf.

Which lines of code should I change in order to replace the xml files with the new ones?

With the System.out.println, I did see that the while loop steps through every directory and compare at every file as expected. A new temp jar was also created as expected... I thought the statement "notInFiles = false" would take care of my need but it's NOT.

How do I step into the /conf and only replace those two files and NOT leave a copy at the root of the jar file?

What am I missing? Thanks for any insight!

Below are the code from that link.

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();
}
}
4

2 回答 2

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()));
于 2014-10-02T07:25:53.283 回答
1

对于带有协议的 URI jar:file:(可用于所有 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 的一种方法是将“jar:”添加到File.toURI().

这更加优雅和抽象,并且还允许 Files.copy 进出 zip。一些东西可以放在工具箱里。

于 2014-10-02T07:38:24.160 回答