在 Java 中,如何批量读取本项目资源目录下的所有文件
在 Java 中,如何批量讀取本項目資源目錄下的所有文件
- 讀取資源目錄下的指定文件
- 方法 1:使用 JDK 中原始 API
- 方法 2:借助 Spring
- 附錄
- 將 InputStream 轉化為 byte 數組
- 將 File 轉化為 byte 數組
- 將 byte 數組轉化為 InputStream
今天筆者遇到一個需求,如何讀取本項目資源目錄下的所有文件。讀取資源目錄下的指定的文件很容易,這只需要給出路徑即可。
讀取資源目錄下的指定文件
/*** 方法 getResourceAsStream 的路徑是以資源目錄 resources 為基準的,* 且不受 Maven 模塊的限制。這于 xxx.class 中 xxx 是哪個模塊的哪個類無關*/ var inputStream = XXX.class.getClassLoader().getResourceAsStream("path/filename.suffix");但是現在,需要讀取資源目錄下的所有文件。也就是說,不能指定資源目錄下的文件名,最多可以指定基路徑。
筆者梳理了一下,這種需求可以分為以下幾個實現步驟:
-
讀取本目錄下的本級文件與文件夾。
-
遞歸讀取本目錄下本級文件夾內的文件。
這樣梳理之后,實現方案就有了。
方法 1:使用 JDK 中原始 API
/*** 讀取資源目錄中路徑 path 下的所有文件。path 不需要以斜杠 / 開頭** @since 2022-1-16*/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);}}/*** 讀取文件或遞歸讀取文件夾內的文件** @since 2022-1-16*/public static void recursiveReadFile(File fileOrDir) {// 如果一開始傳入的 fileOrDir 就為空,直接返回if (fileOrDir == null) {System.out.println("fileOrDir 為空"); // FIXME:日志return;}if (fileOrDir.isFile()) {// 子遞歸函數將在此處終止System.out.println("文件路徑:" + fileOrDir); // TODO:對 fileOrDir 進行處理} else {for (var file : Objects.requireNonNull(fileOrDir.listFiles())) {recursiveReadFile(file); // 如果不需要遞歸遍歷子文件夾,可將此處改為對 fileOrDir 的直接處理}}}??在上面的代碼中,方法 readAllResFiles 中入口方法,然后對于具體的業務,可以在方法 recursiveReadFile 中添加。如果使用函數式編程的技術,對外暴露一個需使用者實現的函數式接口,這里都可以直接在無需提前知道具體業務的情況下,以開閉原則完成本方法的實現。關于函數式編程,可見筆者的另一篇博客:
Java 函數式編程入門:
https://blog.csdn.net/wangpaiblog/article/details/122762637
方法 2:借助 Spring
??筆者正當為自己的聰明機智洋洋得意的時候,突然想到一個問題。掃描項目中某目錄下的所有文件,這不是 Spring 的基本功能嗎?(因為 Spring 支持掃描項目某目錄及其子目錄下的所有文件,并按要求選擇將其注冊為 Bean。)也就是說,Spring 應該已經實現了這種功能。
??為了打消這個顧慮,筆者不斷查看 Spring 源碼,終于發現了這個功能的實現。為了便于閱讀,筆者將源碼解析的部分置于文末,對源碼有興趣的讀者可以看看。
??筆者已經幫讀者在源碼中找到了這個類,讀者可以不用花大量的時間來找了,這個類就是 PathMatchingResourcePatternResolver。它有一個方法 getResources 可以實現此功能。具體代碼如下:
/*** @since 2022-1-16*/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>【源碼解析】
??Spring 在啟動時會自動掃描所有的 Bean。對于 Spring Boot,它會使用 Bean 掃描器(ClassPathBeanDefinitionScanner)掃描基包(basePackage)目錄下的所有 Bean。具體來說,它會先生成基包路徑,然后使用上面的 PathMatchingResourcePatternResolver 進行掃描。
附錄
??上面的方法是間接得到了資源文件,通常還需要做進一步的轉化,這里給出一些常用的轉化操作。
將 InputStream 轉化為 byte 數組
/*** 此方法不會重置流,因此經過轉化后,原來的流將不能再使用。* 因此,如果想要反復讀取流,請轉化前對流進行備份** @since 2021-11-24*/public static byte[] inputStream2byteArray(InputStream inputStream) throws IOException {return inputStream.readAllBytes();}??注意:InputStream 對象中的數據是不能直接被反復讀取的。通常情況下,InputStream 對象在讀取完數據之后將作廢。有一些方法可以解決這個問題,可見筆者的另一篇博客:
如何反復讀取同一個 InputStream 對象:
https://blog.csdn.net/wangpaiblog/article/details/121369433
將 File 轉化為 byte 數組
/*** @since 2021-11-24*/public static byte[] file2byteArray(File file) throws IOException {return inputStream2byteArray(new FileInputStream(file));}??其中,方法 inputStream2byteArray 是前面已經提供的函數。
將 byte 數組轉化為 InputStream
/*** @since 2021-11-24*/public static InputStream byteArray2InputStream(byte[] bytes) {return new ByteArrayInputStream(bytes);}總結
以上是生活随笔為你收集整理的在 Java 中,如何批量读取本项目资源目录下的所有文件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何使 React 中的 useEffe
- 下一篇: 用 Redis 实现分布式锁(Java