如何读取依赖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
相关产品推荐
相关产品推荐

