【问题标题】:Replacing the MANIFEST.MF file in a JAR programmatically以编程方式替换 JAR 中的 MANIFEST.MF 文件
【发布时间】:2020-09-30 11:19:02
【问题描述】:

我想在创建JARexclude certain Class-Path entries 后修改MANIFEST.MF。为此,我决定使用zip4j。提取似乎工作正常,但要将MANIFEST.MF 文件放回JAR,我使用以下代码:

String metaInfFolderName = "META-INF";
Path extractedManifestFilePath = Paths.get("...");
ZipFile zipFile = new ZipFile("Test-Zip-File.zip");
ZipParameters zipParameters = new ZipParameters();
zipParameters.setDefaultFolderPath(metaInfFolderName);
zipFile.addFile(extractedManifestFilePath.toFile(), zipParameters);

但是,此代码无法按预期工作:父目录最终始终命名为 NF,而不是完整的 META-INF。似乎起始字符被切断了。这可能是什么原因,或者是否有另一种有意义的可能性来替换 JARs(本质上只是 ZIPs)中的文件?

maven 依赖:

<dependency>
    <groupId>net.lingala.zip4j</groupId>
    <artifactId>zip4j</artifactId>
    <version>2.6.1</version>
</dependency>

此外,我尝试使用jar 实用程序,如here 所述,但是当调用命令jar uf MyJAR.jar META-INF/MANIFEST.MF 时,JAR 内的MANIFEST.MF 被删除而不是被替换。通过zip -ur MyJAR.jar "META-INF/MANIFEST.MF" 使用zip 实用程序可以工作,但会损坏JAR 文件,因此它不再可运行:

Error: An unexpected error occurred while trying to open file MyJAR.jar

【问题讨论】:

    标签: java jar zip manifest.mf zip4j


    【解决方案1】:

    你可以这样使用:

    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.URI;
    import java.nio.file.FileSystem;
    import java.nio.file.FileSystems;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.util.*;
    import java.util.jar.Attributes;
    import java.util.jar.Attributes.Name;
    import java.util.jar.Manifest;
    
    public class ManifestManipulator {
    
        public static final void main(String... args) throws IOException {
            if (args.length == 0) {
                throw new IllegalArgumentException("at least the path to the JAR file expected!");
            }
            Path jarPath = Paths.get(args[0]);
            Set<String> attributeNames = args.length > 1 ? new LinkedHashSet<>(List.of(args).subList(1, args.length)) : Set.of();
    
            if (!attributeNames.isEmpty()) {
                ManifestManipulator manifestManipulator = new ManifestManipulator();
                manifestManipulator.removeManifestEntries(jarPath, attributeNames);
            } else {
                System.out.println("Warning: no entries specified to remove!");
            }
        }
    
        private void removeManifestEntries(Path jarPath, Set<String> attributeNames) throws IOException {
            System.out.println("Going to remove: " + attributeNames);
            try (FileSystem jarFS = FileSystems.newFileSystem(URI.create("jar:" + jarPath.toUri()), Map.of())) {
                Path manifestPath = jarFS.getPath("META-INF", "MANIFEST.MF");
                Manifest manifest = readManifest(manifestPath);
                Attributes mainAttributes = manifest.getMainAttributes();
                System.out.println("Found main attribute names: " + mainAttributes.keySet());
    
                boolean removed = mainAttributes.entrySet().removeIf(entry -> attributeNames.contains(((Name) entry.getKey()).toString()));
                if (removed) {
                    writeManifest(manifestPath, manifest);
                } else {
                    System.out.println("Warning: nothing removed");
                }
            }
        }
    
        private Manifest readManifest(Path manifestPath) throws IOException {
            try (InputStream is = Files.newInputStream(manifestPath)) {
                return new Manifest(is);
            }
        }
    
        private void writeManifest(Path manifestPath, Manifest manifest) throws IOException {
            try (OutputStream os = Files.newOutputStream(manifestPath)) {
                manifest.write(os);
            }
        }
    
    }
    

    请确保您已添加 jdk.zipfs 模块,该模块将为 ZIP/JAR 文件提供 FileSystemProvider(请参阅 technote)。

    【讨论】:

    • 我实际上仍在使用Java 8,您的代码或类似的东西在那个版本下仍然有效吗?如果没有,其实也没关系,我当然可以迁移到更新的 Java 版本。
    • @BullyWiiPlaza 它也应该与 Java 8 一起工作,并进行一些小的更改(将 List.of() 替换为 Arrays.asList() 等)。至少 Oracle JDK 8 已经在类路径中提供了开箱即用的 ZIP/JAR FileSystemProvider(因此它比 Java 11 更简单)。
    【解决方案2】:

    您不需要任何外部依赖项即可使用 Java 8 更改 ZIP 或 JAR 文件的内容。

    public static void main(String[] args) throws IOException {
        Path zipFilePath = Paths.get("path/to/my/app.jar");
        try (FileSystem zipFileSystem = FileSystems.newFileSystem(zipFilePath, null)) {
            Path manifestFile = zipFileSystem.getPath("META-INF/MANIFEST.MF");
            String newManifestContent;
            // Read from MANIFEST.MF.
            try (Stream<String> lines = Files.lines(manifestFile, StandardCharsets.UTF_8)) {
                newManifestContent = lines.filter(l -> !l.startsWith("Class-Path entry I want to remove"))
                        .collect(Collectors.joining("\n"));
            }
            // Replace MANIFEST.MF content.
            Files.write(manifestFile, newManifestContent.getBytes(StandardCharsets.UTF_8), StandardOpenOption.TRUNCATE_EXISTING);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-08
      • 2010-12-15
      • 2011-09-29
      • 2012-12-13
      • 1970-01-01
      • 2015-03-25
      • 2012-04-24
      • 2013-11-07
      相关资源
      最近更新 更多