Java – 从资源文件夹中读取文件

分手后的思念是犯贱 2023-09-26 09:36 131阅读 0赞

在Java中,我们可以使用getResourceAsStreamgetResourceresources文件夹或类路径的根读取一个或多个文件。

format_png

getResourceAsStream方法返回一个InputStream .

  1. // the stream holding the file content
  2. InputStream is = getClass().getClassLoader().getResourceAsStream("file.txt");
  3. // for static access, uses the class name directly
  4. InputStream is = JavaClassName.class.getClassLoader().getResourceAsStream("file.txt");

getResource方法返回 一个URL,通常将其转换为File ;在 JAR 文件中不起作用

  1. // get the file url, not working in JAR file.
  2. URL resource = getClass().getClassLoader().getResource("file.txt");
  3. if (resource == null) {
  4. throw new IllegalArgumentException("file not found!");
  5. } else {
  6. // failed if files have whitespaces or special characters
  7. //return new File(resource.getFile());
  8. return new File(resource.toURI());
  9. }
  10. // for static access
  11. // URL resource = JavaClassName.class.getClassLoader().getResource("fileName");

1. 资源文件夹中的文件

1.1 查看src/main/resources中的文件,稍后我们将访问这些文件并打印出文件内容。

b9a57fc5f3274c0eb67e34db22e1bba4.webp

src/main/resources/database.properties

  1. datasource.url=jdbc:mysql://localhost/favtuts?useSSL=false
  2. datasource.username=root
  3. datasource.password=password
  4. datasource.driver-class-name=com.mysql.jdbc.Driver

src/main/resources/json/file1.json

  1. {
  2. "name": "favtuts",
  3. "age": 38
  4. }

src/main/resources/json/file2.json

  1. {
  2. "name": "jack",
  3. "age": 40
  4. }

src/main/resources/json/sub/subfile1.json

  1. {
  2. "name": "sub",
  3. "age": 99
  4. }

1.2 默认情况下,构建工具(如 Maven、Gradle 或常见的 Java 实践)会将所有文件从src/main/resources复制到target/classesbuild/classes的根目录。因此,当我们尝试从src/main/resources读取文件时,我们从项目类路径的根读取该文件。

format_png

1.3 下面是一个 JAR 文件结构。通常,resources文件夹中的文件将复制到类路径的根目录。

  1. $ jar -tf target/example.jar
  2. META-INF/MANIFEST.MF
  3. META-INF/
  4. json/
  5. json/sub/
  6. json/file2.json
  7. json/sub/subfile1.json
  8. json/file1.json
  9. database.properties
  10. com/
  11. com/favtuts/
  12. com/favtuts/io/
  13. com/favtuts/io/utils/
  14. //...

2. 从资源文件夹中获取文件。

2.1 下面的示例演示如何使用getResourceAsStreamgetResource方法从resources文件夹中读取json/file1.json文件并打印出文件内容。

注意

  • getResource方法在 JAR 文件中不起作用。
  • getResourceAsStream方法在任何地方都有效。

FileResourcesUtils.java

  1. package com.favtuts.io.utils;
  2. import java.io.BufferedReader;
  3. import java.io.File;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.io.InputStreamReader;
  7. import java.net.URISyntaxException;
  8. import java.net.URL;
  9. import java.nio.charset.StandardCharsets;
  10. import java.nio.file.Files;
  11. import java.util.List;
  12. public class FileResourcesUtils {
  13. public static void main(String[] args) throws Exception {
  14. FileResourcesUtils app = new FileResourcesUtils();
  15. //String fileName = "database.properties";
  16. String fileName = "json/file1.json";
  17. System.out.println("getResourceAsStream : " + fileName);
  18. InputStream is = app.getFileFromResourceAsStream(fileName);
  19. printInputStream(is);
  20. System.out.println("\ngetResource : " + fileName);
  21. File file = app.getFileFromResource(fileName);
  22. printFile(file);
  23. }
  24. // get a file from the resources folder
  25. // works everywhere, IDEA, unit test and JAR file.
  26. private InputStream getFileFromResourceAsStream(String fileName) {
  27. // The class loader that loaded the class
  28. ClassLoader classLoader = getClass().getClassLoader();
  29. InputStream inputStream = classLoader.getResourceAsStream(fileName);
  30. // the stream holding the file content
  31. if (inputStream == null) {
  32. throw new IllegalArgumentException("file not found! " + fileName);
  33. } else {
  34. return inputStream;
  35. }
  36. }
  37. /*
  38. The resource URL is not working in the JAR
  39. If we try to access a file that is inside a JAR,
  40. It throws NoSuchFileException (linux), InvalidPathException (Windows)
  41. Resource URL Sample: file:java-io.jar!/json/file1.json
  42. */
  43. private File getFileFromResource(String fileName) throws URISyntaxException{
  44. ClassLoader classLoader = getClass().getClassLoader();
  45. URL resource = classLoader.getResource(fileName);
  46. if (resource == null) {
  47. throw new IllegalArgumentException("file not found! " + fileName);
  48. } else {
  49. // failed if files have whitespaces or special characters
  50. //return new File(resource.getFile());
  51. return new File(resource.toURI());
  52. }
  53. }
  54. // print input stream
  55. private static void printInputStream(InputStream is) {
  56. try (InputStreamReader streamReader =
  57. new InputStreamReader(is, StandardCharsets.UTF_8);
  58. BufferedReader reader = new BufferedReader(streamReader)) {
  59. String line;
  60. while ((line = reader.readLine()) != null) {
  61. System.out.println(line);
  62. }
  63. } catch (IOException e) {
  64. e.printStackTrace();
  65. }
  66. }
  67. // print a file
  68. private static void printFile(File file) {
  69. List<String> lines;
  70. try {
  71. lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
  72. lines.forEach(System.out::println);
  73. } catch (IOException e) {
  74. e.printStackTrace();
  75. }
  76. }
  77. }

输出

  1. getResourceAsStream : json/file1.json
  2. {
  3. "name": "favtuts",
  4. "age": 38
  5. }
  6. getResource : json/file1.json
  7. {
  8. "name": "favtuts",
  9. "age": 38
  10. }

2.2 现在,我们将项目打包到一个JAR文件中并运行它;这一次,getResource将失败并返回NoSuchFileExceptionInvalidPathException。我们无法通过资源 URL 读取 JAR 文件中的文件。

在 Linux (Ubuntu) 上运行 JAR 文件,它会引发NoSuchFileException

  1. $ mvn clean package
  2. $ java -jar target/java-io.jar
  3. $ jar tf target/java-io.jar | more
  4. $ java -cp target/java-io.jar com.favtuts.io.utils.FileResourcesUtils
  5. getResourceAsStream : json/file1.json
  6. {
  7. "name": "favtuts",
  8. "age": 38
  9. }
  10. # for new File(resource.getFile());
  11. getResource : json/file1.json
  12. java.nio.file.NoSuchFileException: file:/home/favtuts/projects/core-java/java-io/target/java-io.jar!/json/file1.json
  13. at java.base/sun.nio.fs.UnixException.translateToIOException(UnixException.java:92)
  14. at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:111)
  15. at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:116)
  16. at java.base/sun.nio.fs.UnixFileSystemProvider.newByteChannel(UnixFileSystemProvider.java:219)
  17. at java.base/java.nio.file.Files.newByteChannel(Files.java:370)
  18. at java.base/java.nio.file.Files.newByteChannel(Files.java:421)
  19. at java.base/java.nio.file.spi.FileSystemProvider.newInputStream(FileSystemProvider.java:420)
  20. at java.base/java.nio.file.Files.newInputStream(Files.java:155)
  21. at java.base/java.nio.file.Files.newBufferedReader(Files.java:2838)
  22. at java.base/java.nio.file.Files.readAllLines(Files.java:3329)
  23. at com.favtuts.io.utils.FileResourcesUtils.printFile(FileResourcesUtils.java:135)
  24. at com.favtuts.io.utils.FileResourcesUtils.main(FileResourcesUtils.java:24)
  25. # for new File(resource.toURI());
  26. getResource : json/file1.json
  27. Exception in thread "main" java.lang.IllegalArgumentException: URI is not hierarchical
  28. at java.base/java.io.File.<init>(File.java:420)
  29. at com.favtuts.io.utils.FileResourcesUtils.getFileFromResource(FileResourcesUtils.java:112)
  30. at com.favtuts.io.utils.FileResourcesUtils.main(FileResourcesUtils.java:29)

您可能会收到以下消息

  1. no main manifest attribute, in "java-io.jar"

要解决此问题,请阅读堆栈溢出:无法执行 jar 文件:“无主清单属性”中的指南

对于 Maven,我们应该使用在 pom 中定义的 maven-jar 插件.xml:

  1. <!-- Make this jar executable -->
  2. <plugin>
  3. <groupId>org.apache.maven.plugins</groupId>
  4. <artifactId>maven-jar-plugin</artifactId>
  5. <version>3.2.0</version>
  6. <configuration>
  7. <excludes>
  8. <exclude>**/log4j.properties</exclude>
  9. </excludes>
  10. <archive>
  11. <manifest>
  12. <addClasspath>true</addClasspath>
  13. <mainClass>com.favtuts.io.utils.FileResourcesUtils</mainClass>
  14. <classpathPrefix>dependency-jars/</classpathPrefix>
  15. </manifest>
  16. </archive>
  17. </configuration>
  18. </plugin>

附言:此示例使用 Maven 插件创建 JAR 文件。

要检查 jar 文件中的主类,请执行以下操作:

  1. $ jar tf target/java-io.jar | more

3. 从资源文件夹获取文件 – 单元测试

3.1 我们将测试资源放在单元测试的src/test/resources文件夹中。通常,测试资源中的文件将复制到target/test-classes文件夹。

format_png 1

src/test/resources/json/file1.json

  1. {
  2. "name": "unit test",
  3. "age": 38
  4. }

src/test/resources/database.properties.properties

  1. datasource.url=jdbc:mysql://localhost/test?useSSL=false
  2. datasource.username=test
  3. datasource.password=password
  4. datasource.driver-class-name=com.mysql.jdbc.Driver

3.2 它的工作方式与我们从src/main/resources中读取文件的方式相同。我们使用相同的getResourceAsStreamgetResource方法从 src/test/resources读取文件.

FileResourcesTest.java

  1. package com.favtuts.io;
  2. import java.io.BufferedReader;
  3. import java.io.File;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.io.InputStreamReader;
  7. import java.net.URISyntaxException;
  8. import java.net.URL;
  9. import java.nio.charset.StandardCharsets;
  10. import java.nio.file.Files;
  11. import java.util.List;
  12. import org.junit.jupiter.api.DisplayName;
  13. import org.junit.jupiter.api.Test;
  14. // Unit test class
  15. public class FileResourcesTest {
  16. @DisplayName("Test loading a JSON file")
  17. @Test
  18. void loadJSONTest() {
  19. String fileName = "json/file1.json";
  20. ClassLoader classLoader = getClass().getClassLoader();
  21. try (InputStream inputStream = classLoader.getResourceAsStream(fileName);
  22. InputStreamReader streamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
  23. BufferedReader reader = new BufferedReader(streamReader)
  24. ) {
  25. String line;
  26. while((line = reader.readLine()) != null) {
  27. System.out.println(line);
  28. }
  29. } catch (IOException e) {
  30. e.printStackTrace();
  31. }
  32. }
  33. @DisplayName("Test loading a properties file")
  34. @Test
  35. void loadPropTest() throws IOException, URISyntaxException {
  36. String fileName = "database.properties";
  37. ClassLoader classLoader = getClass().getClassLoader();
  38. URL resource = classLoader.getResource(fileName);
  39. if (resource == null) {
  40. throw new IllegalArgumentException("file not found! " + fileName);
  41. }
  42. //File file = new File(resource.getFile());
  43. File file = new File(resource.toURI());
  44. List<String> lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
  45. lines.forEach(System.out::println);
  46. }
  47. }

输出

  1. {
  2. "name": "unit test",
  3. "age": 38
  4. }
  5. datasource.url=jdbc:mysql://localhost/test?useSSL=false
  6. datasource.username=test
  7. datasource.password=password
  8. datasource.driver-class-name=com.mysql.jdbc.Driver

4. 从资源文件夹中获取所有文件。(非 JAR 环境)

如果我们不知道确切的文件名,并且想要读取所有文件,包括资源文件夹中的子文件夹文件,我们可以使用NIO Files.walk轻松访问和读取文件。

4.1 以下示例使用Files.walksrc/main/resources/json文件夹中读取所有文件:

FileResourcesUtils.java

p

  1. package com.favtuts.io.utils;
  2. import java.io.*;
  3. import java.net.URISyntaxException;
  4. import java.net.URL;
  5. import java.nio.charset.StandardCharsets;
  6. import java.nio.file.Files;
  7. import java.nio.file.Paths;
  8. import java.util.List;
  9. import java.util.stream.Collectors;
  10. public class FileResourcesUtils {
  11. public static void main(String[] args) throws IOException {
  12. FileResourcesUtils app = new FileResourcesUtils();
  13. // read all files from a resources folder
  14. try {
  15. // files from src/main/resources/json
  16. List<File> result = app.getAllFilesFromResource("json");
  17. for (File file : result) {
  18. System.out.println("file : " + file);
  19. printFile(file);
  20. }
  21. } catch (URISyntaxException | IOException e) {
  22. e.printStackTrace();
  23. }
  24. }
  25. private List<File> getAllFilesFromResource(String folder)
  26. throws URISyntaxException, IOException {
  27. ClassLoader classLoader = getClass().getClassLoader();
  28. URL resource = classLoader.getResource(folder);
  29. // dun walk the root path, we will walk all the classes
  30. List<File> collect = Files.walk(Paths.get(resource.toURI()))
  31. .filter(Files::isRegularFile)
  32. .map(x -> x.toFile())
  33. .collect(Collectors.toList());
  34. return collect;
  35. }
  36. // print a file
  37. private static void printFile(File file) {
  38. List<String> lines;
  39. try {
  40. lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
  41. lines.forEach(System.out::println);
  42. } catch (IOException e) {
  43. e.printStackTrace();
  44. }
  45. }
  46. }

输出

  1. file : /home/favtuts/projects/core-java/java-io/target/classes/json/file1.json
  2. {
  3. "name": "favtuts",
  4. "age": 38
  5. }
  6. file : /home/favtuts/projects/core-java/java-io/target/classes/json/file2.json
  7. {
  8. "name": "jack",
  9. "age": 40
  10. }
  11. file : /home/favtuts/projects/core-java/java-io/target/classes/json/sub/subfile1.json
  12. {
  13. "name": "sub",
  14. "age": 99
  15. }

4.2 但是,示例 4.1 中的标准Files.walk无法直接访问 JAR 文件中的文件,请尝试在 JAR 环境中运行示例 4.1,然后它会引发FileSystemNotFoundException

  1. $ mvn clean package
  2. $ java -jar target/java-io.jar
  3. Exception in thread "main" java.nio.file.FileSystemNotFoundException
  4. at jdk.zipfs/jdk.nio.zipfs.ZipFileSystemProvider.getFileSystem(ZipFileSystemProvider.java:169)
  5. at jdk.zipfs/jdk.nio.zipfs.ZipFileSystemProvider.getPath(ZipFileSystemProvider.java:155)
  6. at java.base/java.nio.file.Path.of(Path.java:208)
  7. at java.base/java.nio.file.Paths.get(Paths.java:97)
  8. at com.favtuts.io.utils.FileResourcesUtils.getAllFilesFromResource(FileResourcesUtils.java:128)
  9. at com.favtuts.io.utils.FileResourcesUtils.main(FileResourcesUtils.java:35)

5. 从资源文件夹中获取所有文件。(JAR 版本)

5.1 此示例演示Files.walk如何通过FileSystemsjar:file:xxx.jar URI读取 JAR 文件内的文件夹 。

这个想法是:

  • 文件使用FileSystems在 JAR 文件中遍历文件夹,并获取所有文件名,请参见getPathsFromResourceJAR()
  • 循环所有文件名,访问并打印每个文件,如示例 2.1,请参阅getFileFromResourceAsStream()

FileResourcesUtils.java

  1. package com.favtuts.io.utils;
  2. import java.io.*;
  3. import java.net.URI;
  4. import java.net.URISyntaxException;
  5. import java.nio.charset.StandardCharsets;
  6. import java.nio.file.FileSystem;
  7. import java.nio.file.FileSystems;
  8. import java.nio.file.Files;
  9. import java.nio.file.Path;
  10. import java.util.Collections;
  11. import java.util.List;
  12. import java.util.stream.Collectors;
  13. public class FileResourcesUtils {
  14. public static void main(String[] args) throws IOException {
  15. FileResourcesUtils app = new FileResourcesUtils();
  16. // Sample 3 - read all files from a resources folder (JAR version)
  17. try {
  18. // get paths from src/main/resources/json
  19. List<Path> result = app.getPathsFromResourceJAR("json");
  20. for (Path path : result) {
  21. System.out.println("Path : " + path);
  22. String filePathInJAR = path.toString();
  23. // Windows will returns /json/file1.json, cut the first /
  24. // the correct path should be json/file1.json
  25. if (filePathInJAR.startsWith("/")) {
  26. filePathInJAR = filePathInJAR.substring(1, filePathInJAR.length());
  27. }
  28. System.out.println("filePathInJAR : " + filePathInJAR);
  29. // read a file from resource folder
  30. InputStream is = app.getFileFromResourceAsStream(filePathInJAR);
  31. printInputStream(is);
  32. }
  33. } catch (URISyntaxException | IOException e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. // get a file from the resources folder
  38. // works everywhere, IDEA, unit test and JAR file.
  39. private InputStream getFileFromResourceAsStream(String fileName) {
  40. // The class loader that loaded the class
  41. ClassLoader classLoader = getClass().getClassLoader();
  42. InputStream inputStream = classLoader.getResourceAsStream(fileName);
  43. // the stream holding the file content
  44. if (inputStream == null) {
  45. throw new IllegalArgumentException("file not found! " + fileName);
  46. } else {
  47. return inputStream;
  48. }
  49. }
  50. // Get all paths from a folder that inside the JAR file
  51. private List<Path> getPathsFromResourceJAR(String folder)
  52. throws URISyntaxException, IOException {
  53. List<Path> result;
  54. // get path of the current running JAR
  55. String jarPath = getClass().getProtectionDomain()
  56. .getCodeSource()
  57. .getLocation()
  58. .toURI()
  59. .getPath();
  60. System.out.println("JAR Path :" + jarPath);
  61. // file walks JAR
  62. URI uri = URI.create("jar:file:" + jarPath);
  63. try (FileSystem fs = FileSystems.newFileSystem(uri, Collections.emptyMap())) {
  64. result = Files.walk(fs.getPath(folder))
  65. .filter(Files::isRegularFile)
  66. .collect(Collectors.toList());
  67. }
  68. return result;
  69. }
  70. // print input stream
  71. private static void printInputStream(InputStream is) {
  72. try (InputStreamReader streamReader = new InputStreamReader(is, StandardCharsets.UTF_8);
  73. BufferedReader reader = new BufferedReader(streamReader)) {
  74. String line;
  75. while ((line = reader.readLine()) != null) {
  76. System.out.println(line);
  77. }
  78. } catch (IOException e) {
  79. e.printStackTrace();
  80. }
  81. }
  82. }

输出

  1. $ java -jar target/java-io.jar
  2. JAR Path :/C:/Users/favtuts/projects/core-java/java-io/target/java-io.jar
  3. Path : /json/file2.json
  4. filePathInJAR : json/file2.json
  5. {
  6. "name": "jack",
  7. "age": 40
  8. }
  9. Path : /json/file1.json
  10. filePathInJAR : json/file1.json
  11. {
  12. "name": "favtuts",
  13. "age": 38
  14. }
  15. Path : /json/sub/subfile1.json
  16. filePathInJAR : json/sub/subfile1.json
  17. {
  18. "name": "sub",
  19. "age": 99
  20. }

下载源代码

$ git clone https://github.com/favtuts/java-core-tutorials-examples

$ cd java-io

发表评论

表情:
评论列表 (有 0 条评论,131人围观)

还没有评论,来说两句吧...

相关阅读