java · 2023-03-31 0

使用 JarFile 或 URL 读取 jar 及嵌套 jar

一、JarFile 与 URL

JarFile 是 Java 提供的一个类,可以读取 jar 包中的所有文件(递归读取),然后提取指定的文件。

1.JarFile

使用 java.util.jar.JarFile 可读取 jar 内部的文件。若读取嵌套 jar 中的一个文件内容,则需要先把嵌套的 jar,写入临时文件,再从临时 jar 文件,读取其中的文件

@Test
public void testJarEntry() throws Exception {
    JarFile jarFile = new JarFile("/home/zxm/IdeaProjects/parent/level1/target/level1-1.0.jar");
    System.out.println("jarFile name:" + jarFile.getName());

    // 遍历内部文件名称
    Enumeration<JarEntry> entries = jarFile.entries();
    while (entries.hasMoreElements()) {
        JarEntry entry = entries.nextElement();
        String name = entry.getName();
        System.out.println("entry name:" + name);
    }
}

@Test
public void testReadJarFile() throws Exception {
    JarFile jarFile = new JarFile("/home/zxm/IdeaProjects/parent/level1/target/level1-1.0.jar");
    JarEntry jarEntry = jarFile.getJarEntry("t1.txt");

    // 去读文件内容
    if (jarEntry != null) {
        BufferedReader br = new BufferedReader(new InputStreamReader(jarFile.getInputStream(jarEntry)));
        String line;
        StringBuilder builder = new StringBuilder();
        while ((line = br.readLine()) != null) {
            builder.append(line);
        }
        br.close();
        System.out.println("content: " + builder);
    } else {
        System.out.println("t1.txt is not exist");
    }
}

@Test
public void testNestedJarFile() throws Exception {
    String outerJarFilePath = "/home/zxm/IdeaProjects/parent/level1/target/level1-1.0.jar";
    String nestedJarFileName = "lib/level2-1.0.jar";
    String targetFileName = "t2.txt";

    JarFile outerJarFile = new JarFile(outerJarFilePath);
    JarEntry nestedJarEntry = outerJarFile.getJarEntry(nestedJarFileName);

    // 去读文件内容
    if (nestedJarEntry != null) {
        // 创建临时文件
        File tempFile = File.createTempFile("inner_jar_", ".jar");
        FileOutputStream tempOs = new FileOutputStream(tempFile);
        InputStream tempIs = outerJarFile.getInputStream(nestedJarEntry);
        byte[] buffer = new byte[1024];
        int len;
        while ((len = tempIs.read(buffer)) > 0) {
            tempOs.write(buffer, 0, len);
        }
        tempOs.close();
        tempIs.close();

        // 打开内嵌JAR文件
        JarFile nestedJarFile = new JarFile(tempFile);
        JarEntry targetEntry = nestedJarFile.getJarEntry(targetFileName);
        if (targetEntry != null) {
            BufferedReader br = new BufferedReader(new InputStreamReader(nestedJarFile.getInputStream(targetEntry)));
            String line;
            StringBuilder builder = new StringBuilder();
            while ((line = br.readLine()) != null) {
                builder.append(line);
            }
            br.close();
            System.out.println("content: " + builder);
        } else {
            System.out.println(targetFileName + " is not exist");
        }
    } else {
        System.out.println(nestedJarFileName + " is not exist");
    }
}

2.URL

java 支持使用 URL 定位到 jar 中一个文件,但不支持读取嵌套 jar 中的一个文件。如要读取嵌套 jar 中的一个文件,可注册 springboot jar Handler 可处理嵌套 jar 中的文件

@Test
public void testURL() throws Exception {
    // 读取文件
    URL url1 = new URL("jar:file:/home/zxm/IdeaProjects/parent/level1/target/level1-1.0.jar!/t1.txt");

    BufferedReader br1 = new BufferedReader(new InputStreamReader(url1.openStream()));
    String line1;
    StringBuilder builder1 = new StringBuilder();
    while ((line1 = br1.readLine()) != null) {
        builder1.append(line1);
    }
    br1.close();
    System.out.println("content: " + builder1);

    // 注册 spring boot 的 JarFile Handler,支持读取嵌套 jar 的文件
    org.springframework.boot.loader.jar.JarFile.registerUrlProtocolHandler();
    URL url2 = new URL("jar:file:/home/zxm/IdeaProjects/parent/level1/target/level1-1.0.jar!/lib/level2-1.0.jar!/t2.txt");

    BufferedReader br2 = new BufferedReader(new InputStreamReader(url2.openStream()));
    String line2;
    StringBuilder builder2 = new StringBuilder();
    while ((line2 = br2.readLine()) != null) {
        builder2.append(line2);
    }
    br2.close();
    System.out.println("content: " + builder2);
}

若使用 springboot 的 jar handler,需要引入 spring-boot-loader

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-loader</artifactId>
    <version>2.1.7.RELEASE</version>
</dependency>