1. Resource 是什么
Resource 不是 Spring Boot 独有引入的概念。
更准确地说:
Resource来自 Spring Framework- 接口全名是
org.springframework.core.io.Resource - Spring Boot 只是建立在 Spring Framework 之上,所以在 Spring Boot 项目里也经常使用它
一句话理解:
Resource是 Spring 对“资源”的统一抽象,用来屏蔽资源来源差异。
这里的“资源”可以是:
- classpath 下的文件
- 文件系统中的文件
- 网络 URL 资源
- 字节数组
- 输入流
而 JDK 本身虽然有 Path、File、URL、InputStream 等对象,但没有提供一个统一的资源接口把它们都抽象到一起。
2. 为什么 Spring 要提供 Resource
如果只用 JDK,处理不同来源的资源时,代码风格通常不一致:
- 本地文件常用
Path/File - 网络地址常用
URL - 读取内容常用
InputStream
Spring 提供 Resource 后,可以统一成类似这种写法:
Resource resource = ...;
InputStream inputStream = resource.getInputStream();
你不一定要先关心它到底来自:
- classpath
- 磁盘文件
- 远程 URL
这就是它最核心的价值。
3. 常见实现类
Spring 中最常见的几个 Resource 实现:
ClassPathResource:读取 classpath 资源FileSystemResource:读取文件系统资源UrlResource:读取 URL 资源ByteArrayResource:把字节数组包装成资源InputStreamResource:把输入流包装成资源
示例:
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
Resource r1 = new ClassPathResource("application.yml");
Resource r2 = new FileSystemResource("D:/data/report.txt");
Resource r3 = new UrlResource(URI.create("https://example.com/a.txt"));
Resource r4 = new ByteArrayResource("hello".getBytes(StandardCharsets.UTF_8));
Resource r5 = new InputStreamResource(
new ByteArrayInputStream("stream-data".getBytes(StandardCharsets.UTF_8))
);
4. Resource 最常用的方法
4.1 判断资源是否存在
Resource resource = new ClassPathResource("application.yml");
System.out.println(resource.exists());
示例结果:
true
4.2 获取输入流读取内容
这是最常见、最通用的方式。
Resource resource = new ClassPathResource("application.yml");
try (InputStream inputStream = resource.getInputStream()) {
String text = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
System.out.println(text);
}
示例结果:
server:
port: 8080
4.3 获取文件名
Resource resource = new FileSystemResource("D:/data/report.txt");
System.out.println(resource.getFilename());
示例结果:
report.txt
4.4 获取资源描述
Resource resource = new ClassPathResource("application.yml");
System.out.println(resource.getDescription());
示例结果:
class path resource [application.yml]
4.5 获取 URL / URI / File
Resource resource = new FileSystemResource("D:/data/report.txt");
System.out.println(resource.getURL());
System.out.println(resource.getURI());
System.out.println(resource.getFile().getAbsolutePath());
示例结果:
file:/D:/data/report.txt
file:/D:/data/report.txt
D:\data\report.txt
注意:
getInputStream()最通用getFile()不是所有资源都能拿到真实文件- 比如 classpath 资源打进 jar 后,
getFile()可能失败
所以开发中优先记住:
读内容优先用
getInputStream(),不要默认资源一定能转成File。
5. Resource 和 JDK 常见对象的关系
可以把它们理解成这样:
- JDK 的
Path:路径抽象 - JDK 的
File:文件系统对象 - JDK 的
URL:网络或定位资源的地址 - JDK 的
InputStream/OutputStream:读写数据流 - Spring 的
Resource:对多种资源来源的统一抽象
一句话总结:
JDK 负责底层对象建模,Spring
Resource负责做上层统一封装。
6. Resource 与 JDK 之间如何快速转换
这一部分最实用。
6.1 Resource -> InputStream
Resource resource = new ClassPathResource("application.yml");
try (InputStream inputStream = resource.getInputStream()) {
String text = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
System.out.println(text);
}
适用场景:
- 读取配置文件
- 读取模板文件
- 读取 classpath 资源
6.2 Resource -> File
Resource resource = new FileSystemResource("D:/data/report.txt");
File file = resource.getFile();
注意:
- 只有资源确实对应物理文件时才适合这样做
- 对于 jar 包内部资源,不要依赖这个方式
6.3 Resource -> Path
如果你使用的是较新的 Spring Framework 版本,可以直接拿到 Path:
Resource resource = new FileSystemResource("D:/data/report.txt");
Path path = resource.getFilePath();
兼容旧版本时,常见写法仍然是:
Resource resource = new FileSystemResource("D:/data/report.txt");
Path path = resource.getFile().toPath();
说明:
- Spring 没有提供
resource.toPath()这种方法名 - 当前 Spring Framework 新版本提供了
getFilePath() - 较旧版本项目里,常见写法仍然是先转
File,再转Path - 无论哪种方式,这都更适合“真实文件资源”
- 对于 jar 内部 classpath 资源,仍应优先使用
getInputStream()
6.4 Resource -> URL
Resource resource = new UrlResource("https://example.com/demo.txt");
URL url = resource.getURL();
6.5 File -> Resource
File file = new File("D:/data/report.txt");
Resource resource = new FileSystemResource(file);
6.6 Path -> Resource
Path path = Path.of("D:/data/report.txt");
Resource resource = new FileSystemResource(path);
6.7 URL / URI -> Resource
Resource r1 = new UrlResource("https://example.com/demo.txt");
Resource r2 = new UrlResource(URI.create("https://example.com/demo.txt"));
6.8 byte[] -> Resource
byte[] data = "hello".getBytes(StandardCharsets.UTF_8);
Resource resource = new ByteArrayResource(data);
6.9 InputStream -> Resource
InputStream inputStream = new ByteArrayInputStream("hello".getBytes(StandardCharsets.UTF_8));
Resource resource = new InputStreamResource(inputStream);
注意:
InputStreamResource更适合“一次性消费”的流- 它更像“已经打开的流的包装”,不适合长期保存后再反复读取
- 如果内容要反复读取,通常
ByteArrayResource更稳妥
7. 和 JDK 输入输出流之间如何快速转换
7.1 Resource -> InputStream
Resource resource = new ClassPathResource("application.yml");
try (InputStream in = resource.getInputStream()) {
String text = new String(in.readAllBytes(), StandardCharsets.UTF_8);
System.out.println(text);
}
7.2 InputStream -> Resource
InputStream in = new ByteArrayInputStream("abc".getBytes(StandardCharsets.UTF_8));
Resource resource = new InputStreamResource(in);
7.3 byte[] 作为中转:InputStream <-> Resource
如果你希望资源内容可以重复读取,最实用的方法通常是先转成字节数组。
byte[] bytes;
try (InputStream in = new ByteArrayInputStream("abc".getBytes(StandardCharsets.UTF_8))) {
bytes = in.readAllBytes();
}
Resource resource = new ByteArrayResource(bytes);
try (InputStream again = resource.getInputStream()) {
System.out.println(new String(again.readAllBytes(), StandardCharsets.UTF_8));
}
示例结果:
abc
7.4 Resource -> OutputStream 该怎么理解
严格说,Resource 更偏向“读资源描述”,它本身没有一个统一的 getOutputStream() 方法。
如果你想写出内容,通常用下面两种思路:
方式一:转成 WritableResource
Spring 里有些资源同时实现了 WritableResource。
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.WritableResource;
WritableResource resource = new FileSystemResource("D:/data/out.txt");
try (OutputStream out = resource.getOutputStream()) {
out.write("hello".getBytes(StandardCharsets.UTF_8));
}
示例结果:
文件 out.txt 被写入 hello
方式二:直接回到 JDK 的 Files / OutputStream
如果你拿到的是文件资源,也可以转回 JDK 再写。
Resource resource = new FileSystemResource("D:/data/out.txt");
Path path = resource.getFile().toPath();
try (OutputStream out = Files.newOutputStream(path)) {
out.write("hello".getBytes(StandardCharsets.UTF_8));
}
8. 开发中最常见的几个场景
8.1 读取 classpath 配置文件
Resource resource = new ClassPathResource("application.yml");
try (InputStream in = resource.getInputStream()) {
String text = new String(in.readAllBytes(), StandardCharsets.UTF_8);
System.out.println(text);
}
适合:
- 读取模板
- 读取初始化 SQL
- 读取配置文件
8.2 把上传内容包装成 Resource
byte[] bytes = "upload-content".getBytes(StandardCharsets.UTF_8);
Resource resource = new ByteArrayResource(bytes);
try (InputStream in = resource.getInputStream()) {
System.out.println(new String(in.readAllBytes(), StandardCharsets.UTF_8));
}
示例结果:
upload-content
8.3 文件系统资源读写
WritableResource resource = new FileSystemResource("D:/data/log.txt");
try (OutputStream out = resource.getOutputStream()) {
out.write("line-1\n".getBytes(StandardCharsets.UTF_8));
}
try (InputStream in = resource.getInputStream()) {
System.out.println(new String(in.readAllBytes(), StandardCharsets.UTF_8));
}
示例结果:
line-1
9. 使用时的注意点
9.1 不要默认所有 Resource 都能转成 File
这是最常见误区。
例如:
ClassPathResource在开发环境里可能能拿到文件- 但项目打成 jar 后,不一定还能
getFile()成功
所以:
读取内容优先用
getInputStream(),兼容性最好。
9.2 InputStreamResource 不适合反复读取
因为它底层就是一个输入流对象。
如果流已经消费过,再读可能失败。
而且它更适合表示一个“已经打开的资源”,不适合作为可长期保存、可重复打开的资源描述符。
若希望多次读取,优先考虑:
Resource resource = new ByteArrayResource(bytes);
9.3 写操作要看是否是 WritableResource
不是所有 Resource 都支持写。
一般:
FileSystemResource更适合写ClassPathResource通常只读
10. 一份适合快速记忆的总结
如果只记最核心的内容,可以记下面这几句:
来源
Resource来自 Spring Framework- 不是 Spring Boot 独有
作用
- 统一抽象 classpath、文件、URL、字节数组、输入流等资源
最常用方法
exists()getInputStream()getFilename()getDescription()getURL()getURI()getFile()getFilePath()(新版本可用)
最常见实现
ClassPathResourceFileSystemResourceUrlResourceByteArrayResourceInputStreamResource
最重要的实践经验
能用
getInputStream()就优先用它,不要默认所有资源都能安全转成File。
11. 你可以怎么理解 Path、Files、Resource
最后用一句话把它们串起来:
Path:表示路径Files:操作文件Resource:统一描述资源来源
如果做的是 JDK 原生文件处理,重点通常是:
PathFilesInputStreamOutputStream
如果做的是 Spring 项目资源处理,重点通常会变成:
ResourceInputStreamWritableResource- 需要时再回到
File/Path