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

如何读取依赖jar包中指定目录下未知文件名的全部资源文件

Jar包内classpath资源目录遍历解决方案

问题根因

  • 第一种方案失败:你获取到的目录InputStream本质是Jar压缩包的二进制流,普通的文件流读取逻辑无法直接解析压缩包内的层级结构,所以无法直接提取目录下的子文件。
  • 第二种方案失败:本地开发时resources目录是文件系统上的真实路径,可以通过File API直接读取;打为Jar包后所有资源都被打包到压缩包内部,不存在真实的文件系统路径,跨项目引用时类加载器上下文变化也会导致资源查找路径错位。

可行方案

方案1:预生成资源清单(最稳定,全场景兼容)

在依赖Jar的构建阶段自动生成files目录下的所有xml文件名清单,存为固定的资源文件files/index.list随Jar一起打包,运行时直接读取清单获取所有文件名即可。

依赖Jar核心代码实现

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

public class ResourceLoader {
    public Object getData() throws IOException {
        List<String> xmlFileNames = new ArrayList<>();
        // 用当前类的类加载器读取,避免跨项目类加载器上下文问题
        ClassLoader classLoader = this.getClass().getClassLoader();

        // 1. 读取文件名清单
        try (InputStream indexIs = classLoader.getResourceAsStream("files/index.list")) {
            if (indexIs == null) {
                throw new RuntimeException("找不到资源目录清单文件");
            }
            new BufferedReader(new InputStreamReader(indexIs, StandardCharsets.UTF_8))
                    .lines()
                    .filter(s -> s != null && !s.trim().isEmpty())
                    .forEach(xmlFileNames::add);
        }

        // 2. 循环读取所有xml文件解析
        for (String fileName : xmlFileNames) {
            try (InputStream xmlIs = classLoader.getResourceAsStream("files/" + fileName)) {
                if (xmlIs == null) continue;
                // 此处填写你的xml解析、对象填充逻辑
            }
        }

        // 返回填充完成的对象
        return new Object();
    }
}

Maven自动生成清单配置(无需手动维护)

在依赖Jar的pom.xml中添加插件配置,构建时自动扫描files目录下的xml文件生成index.list:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-antrun-plugin</artifactId>
    <version>3.1.0</version>
    <executions>
        <execution>
            <phase>process-resources</phase>
            <goals>
                <goal>run</goal>
            </goals>
            <configuration>
                <target>
                    <fileset id="xmlFiles" dir="${project.build.outputDirectory}/files" includes="*.xml"/>
                    <pathconvert pathsep="${line.separator}" property="xmlFileNames" refid="xmlFiles">
                        <map from="${project.build.outputDirectory}/files/" to=""/>
                    </pathconvert>
                    <echo file="${project.build.outputDirectory}/files/index.list" message="${xmlFileNames}"/>
                </target>
            </configuration>
        </execution>
    </executions>
</plugin>

方案2:运行时动态遍历Jar资源流

如果不想修改构建配置,可以在代码中直接解析Jar流获取目录下的所有文件,需要兼容本地开发和Jar包运行两种场景:

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class ResourceLoader {
    public Object getData() throws IOException, URISyntaxException {
        List<String> xmlFileNames = new ArrayList<>();
        ClassLoader classLoader = this.getClass().getClassLoader();
        String dirPath = "files/";
        URL dirUrl = classLoader.getResource(dirPath);
        if (dirUrl == null) {
            throw new RuntimeException("找不到资源目录");
        }

        // 适配Jar包内资源场景
        if (dirUrl.getProtocol().equals("jar")) {
            String jarPath = dirUrl.getPath().split("!")[0].substring(5);
            jarPath = URLDecoder.decode(jarPath, StandardCharsets.UTF_8.name());
            try (JarFile jarFile = new JarFile(jarPath)) {
                Enumeration<JarEntry> entries = jarFile.entries();
                while (entries.hasMoreElements()) {
                    JarEntry entry = entries.nextElement();
                    String entryName = entry.getName();
                    if (entryName.startsWith(dirPath) && entryName.endsWith(".xml") && !entry.isDirectory()) {
                        xmlFileNames.add(entryName.substring(dirPath.length()));
                    }
                }
            }
        } 
        // 适配本地开发文件系统场景
        else {
            File dir = new File(dirUrl.toURI());
            File[] files = dir.listFiles(f -> f.isFile() && f.getName().endsWith(".xml"));
            if (files != null) {
                for (File file : files) {
                    xmlFileNames.add(file.getName());
                }
            }
        }

        // 后续xml读取解析逻辑和方案1一致
        return new Object();
    }
}

注意:该方案在Spring Boot嵌套Fat Jar场景下可能出现兼容性问题,仅建议纯普通Java项目场景使用,涉及Spring Boot项目优先选择方案1。

核心注意点

  • 所有资源读取必须使用当前类的类加载器this.getClass().getClassLoader(),不要使用线程上下文类加载器,避免跨项目类加载器切换导致资源查找失败
  • 禁止使用java.io.File类操作Jar包内的资源,统一使用getResourceAsStream接口读取资源流

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.21 21:54:04