无法读取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内容的处理存在错误:
trim()破坏原Manifest格式
代码中使用byteArrayOutputStream.toString().trim()会移除原Manifest内容首尾的空白字符(包括结尾换行),违反了Manifest的格式要求。新增属性后缺少结尾换行
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
相关产品推荐
相关产品推荐

