使用Java读取.jar清单文件

6

我正在尝试通过检查manifest文件中的某些值来确定一个.jar文件是否有效。使用Java读取和解析文件的最佳方法是什么?我考虑使用以下命令来提取文件:

jar -xvf anyjar.jar META-INF/MANIFEST.MF

但我可以像这样做吗:

File manifest = Command.exec("jar -xvf anyjar.jar META-INF/MAINFEST.MF");

然后使用一些缓冲读取器之类的工具来解析文件的行?谢谢任何帮助...

可能是https://dev59.com/D3I95IYBdhLWcg3wsQFm#2198542的重复问题。 - greg-449
3个回答

12

使用jar工具的问题在于需要安装完整的JDK。许多Java用户仅安装了JRE,其中不包括jar

此外,jar必须在用户的PATH中。

因此,我建议使用适当的API,就像这样:

Manifest m = new JarFile("anyjar.jar").getManifest();

那实际上应该更容易!


哦,哇,这确实更有意义。等等,那么我也在使用Command.exec("提取jar的代码")。还有更简单的方法吗? - Kyle
是的。但是在Stackoverflow上已经有一个关于这个问题的提问了。 - Robin Green
到目前为止,我所能找到的只有使用命令提示符,但这个链接似乎可以工作,但看起来有点复杂哈哈。为什么不能像jarfile.run()这样简单呢?http://www.programcreek.com/2012/08/unzip-a-jar-file-in-java-program/ - Kyle
是的,不幸的是它更加复杂。但你可以直接复制粘贴,所以谁在乎呢? - Robin Green

5
java.lang.Package中的Package类具有实现您所需功能的方法。以下是使用Java代码获取清单内容的简单方法:
String t = this.getClass().getPackage().getImplementationTitle();
String v = this.getClass().getPackage().getImplementationVersion();

我将此放入共享实用程序类中的静态方法。该方法接受一个类句柄对象作为参数。这样,我们系统中的任何类都可以在需要时获取自己的清单信息。显然,该方法可以轻松修改为返回值的数组或哈希表。
调用该方法:
    String ver = GeneralUtils.checkImplVersion(this);

在名为GeneralUtils.java的文件中有一个方法:

public static String checkImplVersion(Object classHandle)
{
   String v = classHandle.getClass().getPackage().getImplementationVersion();
   return v;
}

如果您想获取除Package类提供的清单字段值之外的其他字段值(例如您自己的Build-Date),则需获取主属性并逐个查找所需字段。以下代码是从类似问题修改而来,可能是在SO上找到的(我想给它归功于原作者,但我忘记了 - 抱歉)。

在try-catch块中放置此代码,并将classHandle(即“this”或MyClass.class)传递给该方法。 "classHandle"的类型为Class:

  String buildDateToReturn = null;
  try
  {
     String path = classHandle.getProtectionDomain().getCodeSource().getLocation().getPath();
     JarFile jar = new JarFile(path);  // or can give a File handle
     Manifest mf = jar.getManifest();
     final Attributes mattr = mf.getMainAttributes();
     LOGGER.trace(" --- getBuildDate: "
           +"\n\t path:     "+ path
           +"\n\t jar:      "+ jar.getName()
           +"\n\t manifest: "+ mf.getClass().getSimpleName()
           );

     for (Object key : mattr.keySet()) 
     {
        String val = mattr.getValue((Name)key);
        if (key != null && (key.toString()).contains("Build-Date"))
        {
           buildDateToReturn = val;
        }
     }
  }
  catch (IOException e)
  { ... }

  return buildDateToReturn;

1
关于Package方法,必须在清单文件中使用正确的名称(例如“Implementation-Version”)进行定义,这些名称可以在java.util.jar.Attributes.Name类中找到。这方面的文档并不是特别详细。 - Steve Cohen

3
最简单的方法是使用JarURLConnection类:
String className = getClass().getSimpleName() + ".class";
String classPath = getClass().getResource(className).toString();
if (!classPath.startsWith("jar")) {
    return DEFAULT_PROPERTY_VALUE;
}

URL url = new URL(classPath);
JarURLConnection jarConnection = (JarURLConnection) url.openConnection();
Manifest manifest = jarConnection.getManifest();
Attributes attributes = manifest.getMainAttributes();
return attributes.getValue(PROPERTY_NAME);

由于在某些情况下,...class.getProtectionDomain().getCodeSource().getLocation(); 返回的路径带有 vfs:/,因此需要额外处理。

或者,使用 ProtectionDomain:

File file = new File(getClass().getProtectionDomain().getCodeSource().getLocation().toURI());
if (file.isFile()) {
    JarFile jarFile = new JarFile(file);
    Manifest manifest = jarFile.getManifest();
    Attributes attributes = manifest.getMainAttributes();
    return attributes.getValue(PROPERTY_NAME);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接