在 Java 中,如何批量读取本项目资源目录下的所有文件
今天笔者遇到一个需求,如何读取本项目资源目录下的所有文件。读取资源目录下的指定的文件很容易,这只需要给出路径即可。
读取资源目录下的指定文件
var inputStream = XXX.class.getClassLoader().getResourceAsStream("path/filename.suffix");
但是现在,需要读取资源目录下的所有文件。也就是说,不能指定资源目录下的文件名,最多可以指定基路径。
笔者梳理了一下,这种需求可以分为以下几个实现步骤:
-
读取本目录下的本级文件与文件夹。 -
递归读取本目录下本级文件夹内的文件。
这样梳理之后,实现方案就有了。
方法 1:使用 JDK 中原始 API
public static void readAllResFiles(String path) throws IOException, URISyntaxException {
var urlEnumeration = ReadAllResFiles.class.getClassLoader().getResources(path);
while (urlEnumeration.hasMoreElements()) {
var url = urlEnumeration.nextElement();
var fileDir = new File(new URI(url.toString()));
recursiveReadFile(fileDir);
}
}
public static void recursiveReadFile(File fileOrDir) {
if (fileOrDir == null) {
System.out.println("fileOrDir 为空");
return;
}
if (fileOrDir.isFile()) {
System.out.println("文件路径:" + fileOrDir);
} else {
for (var file : Objects.requireNonNull(fileOrDir.listFiles())) {
recursiveReadFile(file);
}
}
}
??在上面的代码中,方法 readAllResFiles 中入口方法,然后对于具体的业务,可以在方法 recursiveReadFile 中添加。如果使用函数式编程的技术,对外暴露一个需使用者实现的函数式接口,这里都可以直接在无需提前知道具体业务的情况下,以开闭原则完成本方法的实现。关于函数式编程,可见笔者的另一篇博客:
Java 函数式编程入门: https://blog.csdn.net/wangpaiblog/article/details/122762637
方法 2:借助 Spring
??笔者正当为自己的聪明机智洋洋得意的时候,突然想到一个问题。扫描项目中某目录下的所有文件,这不是 Spring 的基本功能吗?(因为 Spring 支持扫描项目某目录及其子目录下的所有文件,并按要求选择将其注册为 Bean。)也就是说,Spring 应该已经实现了这种功能。
??为了打消这个顾虑,笔者不断查看 Spring 源码,终于发现了这个功能的实现。唉,刚刚吃的夜宵突然就不香了。
??Spring 有一个类 PathMatchingResourcePatternResolver,它有一个方法 getResources 可以实现此功能。具体代码如下:
public static void readAllResFiles() throws IOException {
var resolver = new PathMatchingResourcePatternResolver();
var resources = resolver.getResources("classpath:path/*");
// var resources = resolver.getResources("classpath:path/*.*");
// var resources = resolver.getResources("classpath:path/**");
for (var resource : resources) {
// TODO:对 resource 进行处理
System.out.println(resource.getFilename()); // 文件名
System.out.println(resource.getURL().getPath()); // 文件绝对路径
System.out.println(resource.getFile()); // File 对象
// 如果 resource 不是目录
if (resource.getFile().isFile()) {
System.out.println(resource.getInputStream()); // InputStream 对象
}
}
}
??对于 SpringBoot 项目,可以直接使用上述的方法而不需要添加其它依赖。如果项目中原来没有使用过 Spring,则需要添加如下依赖(以 Maven 为例):
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
附录
??上面的方法是间接得到了资源文件,通常还需要做进一步的转化,这里给出一些常用的转化操作。
将 InputStream 转化为 byte 数组
public static byte[] inputStream2byteArray(InputStream inputStream) throws IOException {
return inputStream.readAllBytes();
}
??注意:InputStream 对象中的数据是不能直接被反复读取的。通常情况下,InputStream 对象在读取完数据之后将作废。有一些方法可以解决这个问题,可见笔者的另一篇博客:
如何反复读取同一个 InputStream 对象: https://blog.csdn.net/wangpaiblog/article/details/121369433
将 File 转化为 byte 数组
public static byte[] file2byteArray(File file) throws IOException {
return inputStream2byteArray(new FileInputStream(file));
}
??其中,方法 inputStream2byteArray 是前面已经提供的函数。
将 byte 数组转化为 InputStream
public static InputStream byteArray2InputStream(byte[] bytes) {
return new ByteArrayInputStream(bytes);
}
|