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

Posted

技术标签:

【中文标题】如何替换jar文件中某个目录中的某些文件?【英文标题】:how to replacing some files within a certain directory inside a jar file? 【发布时间】:2014-10-02 07:11:11 【问题描述】:

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

我已经阅读了一些帖子。此链接上给出的代码(JarUpdater 类)Updating .JAR's contents from code

对我理解ZipInputStream、ZipOutputStream和ZipEntry等的作用和使用非常有帮助。

但是,当我运行它时,

    我有一个 EOF 异常

[由 mk7 编辑:经过 20 次左右,我发现 jar 文件已损坏。因此,在我将 jar 文件替换为新文件后,EOF 异常就消失了。下面的另外两个问题仍未解决]

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

    这两个新的 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();


【问题讨论】:

【参考方案1】:

在您复制不想替换的条目的第一个周期(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()));

【讨论】:

我只是仔细检查。代码确实有 out.closeEntry();和 in.close(); 这是损坏的 jar 文件。所以在我替换了jar文件后,EOF异常就消失了。我编辑了我原来的问题并删除了我第一次遇到的 EOF 异常问题。但即使是现在,我仍然没有替换这两个文件。 添加了正确检查是否要替换条目的代码(您没有正确检查,因此您没有过滤掉要替换的条目)。 你是对的。我对完整路径不小心。现在效果很好。非常感谢!【参考方案2】:

对于协议为 jar:file:(可用于所有 zip 文件)的 URI,您可以使用 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。工具箱里可以放一些东西。

【讨论】:

太好了。我看过一些关于使用 File.toURI() 的帖子。我还不完全理解它如何以及为什么使用这种方法。我可以列出完整的代码以便从头到尾查看吗?非常感谢! 完成,File.toURI() 前置“file:/”,将 Windows ` into '/' and replaces spaces to %20. Java has a built-in protocol handler for jar:` 转换为任何 zip 格式。

以上是关于如何替换jar文件中某个目录中的某些文件?的主要内容,如果未能解决你的问题,请参考以下文章

R:循环遍历目录中的所有文件,应用列替换命令

打包的jar 替换或修改文件

已知道Excel文件的密码,用JAVA如何读取?我原来用的是jxcell 但是这个包是收费的 有啥可以替换这个的吗

jar包可以直接替换高版本的吗

如何替换java项目的框架jar包中的一个文件?

如何从我的 jar 文件中访问资源文件夹中的文件夹?