使用JAVA从jar文件中读取MANIFEST.MF文件

发布于 2024-09-24 18:26:08 字数 66 浏览 6 评论 0 原文

有什么方法可以读取 jar 文件的内容吗?我想读取清单文件以便找到 jar 文件的创建者和版本。有什么办法可以实现吗?

Is there any way I can read the content of a jar file. I want to read the manifest file in order to find the creator of the jar file and version. Is there any way to achieve it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

带上头具痛哭 2024-10-01 18:26:08

下一个代码应该有所帮助:

JarInputStream jarStream = new JarInputStream(stream);
Manifest mf = jarStream.getManifest();

异常处理留给你了:)

Next code should help:

JarInputStream jarStream = new JarInputStream(stream);
Manifest mf = jarStream.getManifest();

Exception handling is left for you :)

天煞孤星 2024-10-01 18:26:08

我建议进行以下操作:

Package aPackage = MyClassName.class.getPackage();
String implementationVersion = aPackage.getImplementationVersion();
String implementationVendor = aPackage.getImplementationVendor();

其中 MyClassName 可以是您编写的应用程序中的任何类。

I would suggest to make following:

Package aPackage = MyClassName.class.getPackage();
String implementationVersion = aPackage.getImplementationVersion();
String implementationVendor = aPackage.getImplementationVendor();

Where MyClassName can be any class from your application written by you.

温暖的光 2024-10-01 18:26:08

您可以使用这样的方法:

public static String getManifestInfo() {
    Enumeration resEnum;
    try {
        resEnum = Thread.currentThread().getContextClassLoader().getResources(JarFile.MANIFEST_NAME);
        while (resEnum.hasMoreElements()) {
            try {
                URL url = (URL)resEnum.nextElement();
                InputStream is = url.openStream();
                if (is != null) {
                    Manifest manifest = new Manifest(is);
                    Attributes mainAttribs = manifest.getMainAttributes();
                    String version = mainAttribs.getValue("Implementation-Version");
                    if(version != null) {
                        return version;
                    }
                }
            }
            catch (Exception e) {
                // Silently ignore wrong manifests on classpath?
            }
        }
    } catch (IOException e1) {
        // Silently ignore wrong manifests on classpath?
    }
    return null; 
}

要获取清单属性,您可以迭代变量“mainAttribs”,或者如果您知道密钥,则可以直接检索所需的属性。

此代码循环遍历类路径上的每个 jar 并读取每个 jar 的 MANIFEST。如果您知道 jar 的名称,您可能只想查看 URL(如果它包含()您感兴趣的 jar 的名称)。

You could use something like this:

public static String getManifestInfo() {
    Enumeration resEnum;
    try {
        resEnum = Thread.currentThread().getContextClassLoader().getResources(JarFile.MANIFEST_NAME);
        while (resEnum.hasMoreElements()) {
            try {
                URL url = (URL)resEnum.nextElement();
                InputStream is = url.openStream();
                if (is != null) {
                    Manifest manifest = new Manifest(is);
                    Attributes mainAttribs = manifest.getMainAttributes();
                    String version = mainAttribs.getValue("Implementation-Version");
                    if(version != null) {
                        return version;
                    }
                }
            }
            catch (Exception e) {
                // Silently ignore wrong manifests on classpath?
            }
        }
    } catch (IOException e1) {
        // Silently ignore wrong manifests on classpath?
    }
    return null; 
}

To get the manifest attributes, you could iterate over the variable "mainAttribs" or directly retrieve your required attribute if you know the key.

This code loops through every jar on the classpath and reads the MANIFEST of each. If you know the name of the jar you may want to only look at the URL if it contains() the name of the jar you are interested in.

醉生梦死 2024-10-01 18:26:08

我根据 stackoverflow 的一些想法实现了一个 AppVersion 类,这里我只是分享整个类:

import java.io.File;
import java.net.URL;
import java.util.jar.Attributes;
import java.util.jar.Manifest;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class AppVersion {
  private static final Logger log = LoggerFactory.getLogger(AppVersion.class);

  private static String version;

  public static String get() {
    if (StringUtils.isBlank(version)) {
      Class<?> clazz = AppVersion.class;
      String className = clazz.getSimpleName() + ".class";
      String classPath = clazz.getResource(className).toString();
      if (!classPath.startsWith("jar")) {
        // Class not from JAR
        String relativePath = clazz.getName().replace('.', File.separatorChar) + ".class";
        String classFolder = classPath.substring(0, classPath.length() - relativePath.length() - 1);
        String manifestPath = classFolder + "/META-INF/MANIFEST.MF";
        log.debug("manifestPath={}", manifestPath);
        version = readVersionFrom(manifestPath);
      } else {
        String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) + "/META-INF/MANIFEST.MF";
        log.debug("manifestPath={}", manifestPath);
        version = readVersionFrom(manifestPath);
      }
    }
    return version;
  }

  private static String readVersionFrom(String manifestPath) {
    Manifest manifest = null;
    try {
      manifest = new Manifest(new URL(manifestPath).openStream());
      Attributes attrs = manifest.getMainAttributes();

      String implementationVersion = attrs.getValue("Implementation-Version");
      implementationVersion = StringUtils.replace(implementationVersion, "-SNAPSHOT", "");
      log.debug("Read Implementation-Version: {}", implementationVersion);

      String implementationBuild = attrs.getValue("Implementation-Build");
      log.debug("Read Implementation-Build: {}", implementationBuild);

      String version = implementationVersion;
      if (StringUtils.isNotBlank(implementationBuild)) {
        version = StringUtils.join(new String[] { implementationVersion, implementationBuild }, '.');
      }
      return version;
    } catch (Exception e) {
      log.error(e.getMessage(), e);
    }
    return StringUtils.EMPTY;
  }
}

基本上,这个类可以从它自己的 JAR 文件的清单或它的类文件夹中的清单中读取版本信息。希望它可以在不同的平台上运行,但到目前为止我只在 Mac OS X 上进行了测试。

我希望这对其他人有用。

I implemented an AppVersion class according to some ideas from stackoverflow, here I just share the entire class:

import java.io.File;
import java.net.URL;
import java.util.jar.Attributes;
import java.util.jar.Manifest;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class AppVersion {
  private static final Logger log = LoggerFactory.getLogger(AppVersion.class);

  private static String version;

  public static String get() {
    if (StringUtils.isBlank(version)) {
      Class<?> clazz = AppVersion.class;
      String className = clazz.getSimpleName() + ".class";
      String classPath = clazz.getResource(className).toString();
      if (!classPath.startsWith("jar")) {
        // Class not from JAR
        String relativePath = clazz.getName().replace('.', File.separatorChar) + ".class";
        String classFolder = classPath.substring(0, classPath.length() - relativePath.length() - 1);
        String manifestPath = classFolder + "/META-INF/MANIFEST.MF";
        log.debug("manifestPath={}", manifestPath);
        version = readVersionFrom(manifestPath);
      } else {
        String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) + "/META-INF/MANIFEST.MF";
        log.debug("manifestPath={}", manifestPath);
        version = readVersionFrom(manifestPath);
      }
    }
    return version;
  }

  private static String readVersionFrom(String manifestPath) {
    Manifest manifest = null;
    try {
      manifest = new Manifest(new URL(manifestPath).openStream());
      Attributes attrs = manifest.getMainAttributes();

      String implementationVersion = attrs.getValue("Implementation-Version");
      implementationVersion = StringUtils.replace(implementationVersion, "-SNAPSHOT", "");
      log.debug("Read Implementation-Version: {}", implementationVersion);

      String implementationBuild = attrs.getValue("Implementation-Build");
      log.debug("Read Implementation-Build: {}", implementationBuild);

      String version = implementationVersion;
      if (StringUtils.isNotBlank(implementationBuild)) {
        version = StringUtils.join(new String[] { implementationVersion, implementationBuild }, '.');
      }
      return version;
    } catch (Exception e) {
      log.error(e.getMessage(), e);
    }
    return StringUtils.EMPTY;
  }
}

Basically, this class can read version information from the manifest of its own JAR file, or the manifest in it's classes folder. And hopefully it works on different platforms, but I only tested it on Mac OS X so far.

I hope this would be useful for someone else.

半暖夏伤 2024-10-01 18:26:08

通过这种简单的方式实现属性

    public static String  getMainClasFromJarFile(String jarFilePath) throws Exception{
    // Path example: "C:\\Users\\GIGABYTE\\.m2\\repository\\domolin\\DeviceTest\\1.0-SNAPSHOT\\DeviceTest-1.0-SNAPSHOT.jar";
    JarInputStream jarStream = new JarInputStream(new FileInputStream(jarFilePath));
    Manifest mf = jarStream.getManifest();
    Attributes attributes = mf.getMainAttributes();
    // Manifest-Version: 1.0
    // Built-By: GIGABYTE
    // Created-By: Apache Maven 3.0.5
    // Build-Jdk: 1.8.0_144
    // Main-Class: domolin.devicetest.DeviceTest
    String mainClass = attributes.getValue("Main-Class");
    //String mainClass = attributes.getValue("Created-By");
    //  Output: domolin.devicetest.DeviceTest
    return mainClass;
}

Achieve the attributes in this simple way

    public static String  getMainClasFromJarFile(String jarFilePath) throws Exception{
    // Path example: "C:\\Users\\GIGABYTE\\.m2\\repository\\domolin\\DeviceTest\\1.0-SNAPSHOT\\DeviceTest-1.0-SNAPSHOT.jar";
    JarInputStream jarStream = new JarInputStream(new FileInputStream(jarFilePath));
    Manifest mf = jarStream.getManifest();
    Attributes attributes = mf.getMainAttributes();
    // Manifest-Version: 1.0
    // Built-By: GIGABYTE
    // Created-By: Apache Maven 3.0.5
    // Build-Jdk: 1.8.0_144
    // Main-Class: domolin.devicetest.DeviceTest
    String mainClass = attributes.getValue("Main-Class");
    //String mainClass = attributes.getValue("Created-By");
    //  Output: domolin.devicetest.DeviceTest
    return mainClass;
}
怀里藏娇 2024-10-01 18:26:08

您可以使用实用程序类 Manifests 来自 jcabi-manifests

final String value = Manifests.read("My-Version");

该类将查找所有 MANIFEST.MF 文件在类路径中可用,并从其中之一读取您要查找的属性。另请阅读:http://www.yegor256.com/2014/07/03/how-to-read-manifest-mf.html yegor256.com/2014/07/03/how-to-read-manifest-mf.html

You can use a utility class Manifests from jcabi-manifests:

final String value = Manifests.read("My-Version");

The class will find all MANIFEST.MF files available in classpath and read the attribute you're looking for from one of them. Also, read this: http://www.yegor256.com/2014/07/03/how-to-read-manifest-mf.html

浮生未歇 2024-10-01 18:26:08
  1. 阅读版本;
  2. 我们将 MANIFEST.MF 从 jar 复制到用户主目录。
    public void processManifestFile() {
        String version = this.getClass().getPackage().getImplementationVersion();
        LOG.info("Version: {}", version);
        Path targetFile = Paths.get(System.getProperty("user.home"), "my-project", "MANIFEST.MF");

        try {
            URL url = this.getClass().getProtectionDomain().getCodeSource().getLocation();
            JarFile jarFile = new JarFile(url.getFile());
            Manifest manifest = jarFile.getManifest();
            try(FileWriter fw = new FileWriter(targetFile.toFile())) {
                manifest.getMainAttributes().entrySet().stream().forEach( x -> {
                    try {
                        fw.write(x.getKey() + ": " + x.getValue() + "\n");
                        LOG.info("{}: {}", x.getKey(), x.getValue());
                    } catch (IOException e) {
                        LOG.error("error in write manifest, {}", e.getMessage());
                    }
                });
            }
            LOG.info("Copy MANIFEST.MF to {}", targetFile);
        } catch (Exception e) {
            LOG.error("Error in processing MANIFEST.MF file", e);
        }
    }
  1. read version;
  2. we copied MANIFEST.MF from jar to user home.
    public void processManifestFile() {
        String version = this.getClass().getPackage().getImplementationVersion();
        LOG.info("Version: {}", version);
        Path targetFile = Paths.get(System.getProperty("user.home"), "my-project", "MANIFEST.MF");

        try {
            URL url = this.getClass().getProtectionDomain().getCodeSource().getLocation();
            JarFile jarFile = new JarFile(url.getFile());
            Manifest manifest = jarFile.getManifest();
            try(FileWriter fw = new FileWriter(targetFile.toFile())) {
                manifest.getMainAttributes().entrySet().stream().forEach( x -> {
                    try {
                        fw.write(x.getKey() + ": " + x.getValue() + "\n");
                        LOG.info("{}: {}", x.getKey(), x.getValue());
                    } catch (IOException e) {
                        LOG.error("error in write manifest, {}", e.getMessage());
                    }
                });
            }
            LOG.info("Copy MANIFEST.MF to {}", targetFile);
        } catch (Exception e) {
            LOG.error("Error in processing MANIFEST.MF file", e);
        }
    }
沩ん囻菔务 2024-10-01 18:26:08

保持简单。 JAR 也是一个 ZIP,因此任何 ZIP 代码都可用于读取 MAINFEST.MF

public static String readManifest(String sourceJARFile) throws IOException
{
    ZipFile zipFile = new ZipFile(sourceJARFile);
    Enumeration entries = zipFile.entries();

    while (entries.hasMoreElements())
    {
        ZipEntry zipEntry = (ZipEntry) entries.nextElement();
        if (zipEntry.getName().equals("META-INF/MANIFEST.MF"))
        {
            return toString(zipFile.getInputStream(zipEntry));
        }
    }

    throw new IllegalStateException("Manifest not found");
}

private static String toString(InputStream inputStream) throws IOException
{
    StringBuilder stringBuilder = new StringBuilder();
    try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)))
    {
        String line;
        while ((line = bufferedReader.readLine()) != null)
        {
            stringBuilder.append(line);
            stringBuilder.append(System.lineSeparator());
        }
    }

    return stringBuilder.toString().trim() + System.lineSeparator();
}

尽管灵活性,对于仅读取数据这个答案是最好的。

Keep it simple. A JAR is also a ZIP so any ZIP code can be used to read the MAINFEST.MF:

public static String readManifest(String sourceJARFile) throws IOException
{
    ZipFile zipFile = new ZipFile(sourceJARFile);
    Enumeration entries = zipFile.entries();

    while (entries.hasMoreElements())
    {
        ZipEntry zipEntry = (ZipEntry) entries.nextElement();
        if (zipEntry.getName().equals("META-INF/MANIFEST.MF"))
        {
            return toString(zipFile.getInputStream(zipEntry));
        }
    }

    throw new IllegalStateException("Manifest not found");
}

private static String toString(InputStream inputStream) throws IOException
{
    StringBuilder stringBuilder = new StringBuilder();
    try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)))
    {
        String line;
        while ((line = bufferedReader.readLine()) != null)
        {
            stringBuilder.append(line);
            stringBuilder.append(System.lineSeparator());
        }
    }

    return stringBuilder.toString().trim() + System.lineSeparator();
}

Despite the flexibility, for just reading data this answer is the best.

不疑不惑不回忆 2024-10-01 18:26:08

我很惊讶没有其他人提出这个解决方案:

File jar = ...;
try (JarFile jarFile = new JarFile(jar)) {
    Manifest manifest = jarFile.getManifest();
    // ...
}

I'm surprised that no one else proposed this solution:

File jar = ...;
try (JarFile jarFile = new JarFile(jar)) {
    Manifest manifest = jarFile.getManifest();
    // ...
}
沒落の蓅哖 2024-10-01 18:26:08

如果您需要的只是版本,请使用此:

//java
public class Version {
    public static final String ver=Version.class.getPackage().getImplementationVersion();
}//use as Version.ver
//kotlin
object Version{
    val ver=this::class.java.`package`.implementationVersion
}//use as Version.ver

还有 implementationVendorimplementationTitle。对于规范*也是如此。


要读取清单本身,请通过 ClassLoader 将其作为 InputStream 获取,然后初始化 Manifest 对象,eg:

//java
public class DemoJava{
    public static void main(String[] args) {
        try (var ins = ClassLoader.getSystemResourceAsStream(JarFile.MANIFEST_NAME)) {
            if (ins != null) {
                for(var entry:new Manifest(ins).getMainAttributes().entrySet()){
                    System.out.println(entry);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

//kotlin
fun main() {
    ClassLoader.getSystemResourceAsStream(JarFile.MANIFEST_NAME)?.use {
        Manifest(it).mainAttributes.forEach { println(it) }
    }
}

读取清单也允许读取自定义属性,例如发布分支提交 ID(如果已添加)。


要读取 jar 本身,请使用 CodeSource,然后初始化一个 JarFile 对象,例如:

//java
public class DemoJava{
    public static void main(String[] args) {
        var location = DemoJava.class.getProtectionDomain().getCodeSource().getLocation();
        if (location != null) {
            try (var jarFile = new JarFile(location.toURI().getPath())) {
                for(var entry:jarFile.getManifest().getMainAttributes().entrySet()){
                    System.out.println(entry);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
//kotlin
object DummyClass
fun main(){
    DummyClass::class.java.protectionDomain.codeSource.location?.toURI()?.path.let {
        JarFile(it).manifest.mainAttributes.forEach { println(it) }
    }
}

注意:在 IDE 环境中,代码可能从 IDE 的构建目录运行而不是来自打包的 jar/构建的工件,在这种情况下,上述某些方法(或此处的其他答案)可能会产生错误的结果。使用 IDE 的工件/出版物来找到该 jar。

If all you need is version, use this:

//java
public class Version {
    public static final String ver=Version.class.getPackage().getImplementationVersion();
}//use as Version.ver
//kotlin
object Version{
    val ver=this::class.java.`package`.implementationVersion
}//use as Version.ver

There are also implementationVendor and implementationTitle. Similarly for specification*.


To read the manifest itself, obtain it as an InputStream via ClassLoader, then initialize a Manifest object,e.g.:

//java
public class DemoJava{
    public static void main(String[] args) {
        try (var ins = ClassLoader.getSystemResourceAsStream(JarFile.MANIFEST_NAME)) {
            if (ins != null) {
                for(var entry:new Manifest(ins).getMainAttributes().entrySet()){
                    System.out.println(entry);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

//kotlin
fun main() {
    ClassLoader.getSystemResourceAsStream(JarFile.MANIFEST_NAME)?.use {
        Manifest(it).mainAttributes.forEach { println(it) }
    }
}

Reading the manifest also allows reading custom attributes e.g. release branch commit ids (if they were added).


To read the jar itself, obtain its location using CodeSource, then initializa a JarFile object, e.g.:

//java
public class DemoJava{
    public static void main(String[] args) {
        var location = DemoJava.class.getProtectionDomain().getCodeSource().getLocation();
        if (location != null) {
            try (var jarFile = new JarFile(location.toURI().getPath())) {
                for(var entry:jarFile.getManifest().getMainAttributes().entrySet()){
                    System.out.println(entry);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
//kotlin
object DummyClass
fun main(){
    DummyClass::class.java.protectionDomain.codeSource.location?.toURI()?.path.let {
        JarFile(it).manifest.mainAttributes.forEach { println(it) }
    }
}

Note: In an IDE environment, the code may run from the IDE's build directory instead of from a packaged jar/built artifact, in which case some of the above methods (or other answers here) may yield wrong results. Use your IDE's artifacts/publication's to locate the jar.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文