You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无法读取Jar文件中自定义Manifest属性的问题排查

问题描述

需要给外部Jar文件添加自定义Manifest属性Deployments-Version: 1.2.3,用7zip查看Jar中的Manifest已成功添加该属性,但通过java.util.jar.Manifest读取时,调用Attributes.getValue(String)返回null,调试发现Manifest实例中无此属性。

提供的代码如下:

public static void main(String[] args) throws IOException, URISyntaxException {
    File jar = new File("C:\\Users\\employee1234\\Desktop\\auth-0.1.3.jar");
    String testVersion = "1.2.3";

    Map<String, String> env = new HashMap<>();
    env.put("create", "true");

    // Mount the jar
    try (FileSystem fileSystem = FileSystems.newFileSystem(jarFileToURI(jar), env)) {
        // Read the manifest
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        Path manifestPath = fileSystem.getPath("/META-INF/MANIFEST.MF");
        Files.copy(manifestPath, byteArrayOutputStream);

        // Convert the manifest bytes to a string and construct a string builder
        StringBuilder manifestData = new StringBuilder(byteArrayOutputStream.toString().trim());
        // Add the custom manifest attribute
        manifestData.append("\n");
        manifestData.append("Deployments-Version: ");
        manifestData.append(testVersion);

        // Write the manifest back to the jar
        Files.copy(new ByteArrayInputStream(manifestData.toString().getBytes()), manifestPath,
                StandardCopyOption.REPLACE_EXISTING);
        
        // Try-with-resources closes the mounted jar
    }

    // This part doesn't work
    try (JarFile jarFile = new JarFile(jar)) {
        Manifest manifest = jarFile.getManifest();
        System.out.println(manifest.getMainAttributes().getValue("Deployments-Version"));
    }
}

// Stolen from java.io.File with some modifications
private static URI jarFileToURI(File jarFile) throws URISyntaxException {
    String sp = slashify(jarFile.getAbsoluteFile().getPath(), false);
    if (sp.startsWith("//"))
        sp = "//" + sp;
    return new URI("jar:file", null, sp, null);
}

// Stolen from java.io.File;
private static String slashify(String path, boolean isDirectory) {
    String p = path;
    if (File.separatorChar != '/')
        p = p.replace(File.separatorChar, '/');
    if (!p.startsWith("/"))
        p = "/" + p;
    if (!p.endsWith("/") && isDirectory)
        p = p + "/";
    return p;
}

通过7zip查看的Manifest内容:

Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: employee1234
Build-Jdk: 1.8.0_202
Deployments-Version: 1.2.3
问题原因及解决方案

问题出在Manifest文件格式不符合Java解析器规范,以及代码对Manifest内容的处理存在错误:

  1. trim()破坏原Manifest格式
    代码中使用byteArrayOutputStream.toString().trim()会移除原Manifest内容首尾的空白字符(包括结尾换行),违反了Manifest的格式要求。

  2. 新增属性后缺少结尾换行
    Java的Manifest解析器要求文件最后一行必须以换行符结尾,否则会忽略最后一行的属性。你的代码新增属性后未添加结尾换行,导致Deployments-Version行被解析器忽略。

修正方案一:修复字符串拼接逻辑

修改Manifest内容处理部分,保留原格式并确保结尾有换行:

// 读取Manifest时不要用trim(),保留原格式的换行
String originalManifest = byteArrayOutputStream.toString();
StringBuilder manifestData = new StringBuilder(originalManifest);

// 检查原Manifest末尾是否已有换行,避免重复添加
if (!originalManifest.endsWith("\n")) {
    manifestData.append("\n");
}

// 添加自定义属性,确保属性行后有换行
manifestData.append("Deployments-Version: ").append(testVersion).append("\n");

修正方案二:使用Java Manifest API处理(更可靠)

避免直接字符串拼接,改用java.util.jar.Manifest类读取、修改和写入,由API自动维护格式合规性:

try (FileSystem fileSystem = FileSystems.newFileSystem(jarFileToURI(jar), env)) {
    Path manifestPath = fileSystem.getPath("/META-INF/MANIFEST.MF");
    // 用Manifest类读取原文件
    Manifest manifest = new Manifest(Files.newInputStream(manifestPath));
    // 添加自定义属性
    manifest.getMainAttributes().putValue("Deployments-Version", testVersion);
    
    // 写入回Jar
    try (OutputStream os = Files.newOutputStream(manifestPath, StandardOpenOption.TRUNCATE_EXISTING)) {
        manifest.write(os);
    }
}

内容的提问来源于stack exchange,提问作者Cardinal System

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 02:35:37