1、概览 {#1概览}
本文将带你了解 Java 中读取文件的不同方法之间的差异。主要介绍 getResourceAsStream()
方法和 FileInputStream
类,以及它们的用例。
先说结论,Files.newInputStream()
方法,由于其在内存和性能方面的好处,推荐用于替代 FileInputStream
。
2、基础知识 {#2基础知识}
首先来了解一下 getResourceAsStream()
和 FileInputStream
之间的区别以及它们的常见用例。
2.1、使用 getResourceAsStream() 读取文件 {#21使用-getresourceasstream-读取文件}
getResourceAsStream()
方法从 classpath 读取文件。传递给 getResourceAsStream()
方法的文件路径应相对于 classpath 。该方法返回一个可用于读取文件的 InputStream
。
这种方法通常用于读取配置文件、properties 文件和其他与应用打包在一起的资源。
2.2、使用 FileInputStream 读取文件 {#22使用-fileinputstream-读取文件}
FileInputStream
类用于从文件系统中读取文件。当需要读取未与应用打包在一起的文件时(本地磁盘),该类非常有用。
传递给 FileInputStream
构造函数的文件路径应该是绝对路径或与当前工作目录相对的路径。
FileInputStream
对象由于使用了 finalizers
(finalize()
方法),可能存在内存和性能问题。FileInputStream
的更好替代方案是 Files.newInputStream()
方法,其工作方式相同。
本文示例中使用 Files.newInputStream()
方法从文件系统中读取文件。
这些方法通常用于读取文件系统中的外部文件,如日志文件、用户数据文件和 Secret 文件。
3、代码示例 {#3代码示例}
让我们通过一个示例来演示 getResourceAsStream()
和 Files.newInputStream()
的用法。
创建一个简单的工具类,使用这两种方法读取文件。然后,通过从 classpath 和文件系统中读取示例文件来测试这两种方法。
3.1、使用 getResourceAsStream() {#31使用-getresourceasstream}
首先,来看看 getResourceAsStream()
方法的用法。
创建一个名为 FileIOUtil
的类,并添加一个从指定资源中读取文件的方法:
static String readFileFromResource(String filePath) {
try (InputStream inputStream = FileIOUtil.class.getResourceAsStream(filePath)) {
String result = null;
if (inputStream != null) {
result = new BufferedReader(new InputStreamReader(inputStream))
.lines()
.collect(Collectors.joining("\n"));
}
return result;
} catch (IOException e) {
LOG.error("Error reading file:", e);
return null;
}
}
在此方法中,我们通过将文件路径作为参数传递给 getResourceAsStream()
方法来获取 InputStream
。该文件路径应相对于 classpath。然后,使用 BufferedReader
读取文件内容。该方法逐行读取内容,并使用 Collectors.joining()
方法将它们拼接起来。最后,以 String
形式返回文件内容。
如果出现异常,例如找不到文件,则会被 catch 并返回 null
。
3.2、使用 Files.newInputStream() {#32使用-filesnewinputstream}
接下来,使用 Files.newInputStream()
定义一个类似的方法:
static String readFileFromFileSystem(String filePath) {
try (InputStream inputStream = Files.newInputStream(Paths.get(filePath))) {
return new BufferedReader(new InputStreamReader(inputStream))
.lines()
.collect(Collectors.joining("\n"));
} catch (IOException e) {
LOG.error("Error reading file:", e);
return null;
}
}
在此方法中,我们使用 Files.newInputStream()
方法从文件系统中读取文件。文件路径应为绝对路径或项目目录的相对路径。与前一个方法类似,读取并返回文件内容。
4、测试 {#4测试}
现在,通过读取一个示例来测试这两种方法。
注意观察,在不同情况下文件路径是如何传递给方法的。
4.1、从 Classpath 读取文件 {#41从-classpath-读取文件}
首先,比较一下这些方法如何从 classpath 读取文件。
在 src/main/resources
目录下创建一个名为 test.txt
的文件,并在其中添加一些内容:
Hello!
Welcome to the world of Java NIO.
使用两种方法读取该文件并验证其内容:
@Test
void givenFileUnderResources_whenReadFileFromResource_thenSuccess() {
String result = FileIOUtil.readFileFromResource("/test.txt");
assertNotNull(result);
assertEquals(result, "Hello!\n" + "Welcome to the world of Java NIO.");
}
@Test
void givenFileUnderResources_whenReadFileFromFileSystem_thenSuccess() {
String result = FileIOUtil.readFileFromFileSystem("src/test/resources/test.txt");
assertNotNull(result);
assertEquals(result, "Hello!\n" + "Welcome to the world of Java NIO.");
}
我们可以看到,这两种方法都读取了文件 test.txt
,并返回其内容。然后,比较文件内容,确保它们与预期值一致。这两种方法的区别在于作为参数传递的文件路径。
readFileFromResource()
方法需要一个相对于 classpath 的路径。由于文件直接位于 src/main/resources
目录下,因此将 /test.txt
作为文件路径。
而,readFileFromFileSystem()
方法需要一个绝对路径或与当前工作目录相对的路径。我们传递 src/main/resources/test.txt
作为文件路径。或者,也可以传递文件的绝对路径,如 /path/to/project/src/main/resources/test.txt
。
4.2、从文件系统读取文件 {#42从文件系统读取文件}
在项目目录外创建一个名为 external.txt
的文件,并尝试使用这两种方法读取该文件。
创建测试方法,使用这两种方法读取文件:
@Test
void givenFileOutsideResources_whenReadFileFromFileSystem_thenSuccess() {
String result = FileIOUtil.readFileFromFileSystem("../external.txt");
assertNotNull(result);
assertEquals(result, "Hello!\n" + "Welcome to the world of Java NIO.");
}
@Test
void givenFileOutsideResources_whenReadFileFromResource_thenNull() {
String result = FileIOUtil.readFileFromResource("../external.txt");
assertNull(result);
}
如上,我们传递的是 external.txt
文件的相对路径。readFileFromFileSystem()
方法直接从文件系统读取文件并返回其内容。
如果尝试使用 readFileFromResource()
方法读取文件,它将返回空值,因为文件不在 classpath 中。
5、总结 {#5总结}
本文介绍了使用 getResourceAsStream()
从 classpath 读取文件与使用 Files.newInputStream()
从文件系统读取文件之间的区别。
Ref:https://www.baeldung.com/java-getresourceasstream-vs-fileinputstream