Программная замена файла MANIFEST.MF в JAR

Я хочу изменить MANIFEST.MF после создания JAR, чтобы исключить определенные записи Class-Path. Для этого я решил использовать 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, как описано здесь, но когда при вызове команды jar uf MyJAR.jar META-INF/MANIFEST.MF MANIFEST.MF внутри JAR удаляется, а не заменяется. Использование утилиты zip через zip -ur MyJAR.jar "META-INF/MANIFEST.MF" работает, но повреждает файл JAR, поэтому он больше не запускается:

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

person BullyWiiPlaza    schedule 10.06.2020    source источник


Ответы (2)


Вы можете использовать что-то вроде этого:

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, который предоставит FileSystemProvider для файлов ZIP/JAR (см. техническое примечание).

person Puce    schedule 18.06.2020
comment
На самом деле я все еще использую Java 8, работает ли ваш код или что-то подобное в этой версии? Если нет, на самом деле это тоже нормально, я, конечно, могу перейти на более новую версию Java. - person BullyWiiPlaza; 23.06.2020
comment
@BullyWiiPlaza Он также должен работать с Java 8 с некоторыми незначительными изменениями (замените List.of() на Arrays.asList() и т. д.). По крайней мере, Oracle JDK 8 имеет ZIP/JAR FileSystemProvider уже в пути к классам из коробки (так что это даже проще, чем с Java 11). - person Puce; 23.06.2020

Вам не нужна никакая внешняя зависимость для изменения содержимого файла ZIP или JAR с помощью Java 8.

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);
    }
}
person lojoe    schedule 25.06.2020