1. Resource 是什么

Resource 不是 Spring Boot 独有引入的概念。

更准确地说:

  • Resource 来自 Spring Framework
  • 接口全名是 org.springframework.core.io.Resource
  • Spring Boot 只是建立在 Spring Framework 之上,所以在 Spring Boot 项目里也经常使用它

一句话理解:

Resource 是 Spring 对“资源”的统一抽象,用来屏蔽资源来源差异。

这里的“资源”可以是:

  • classpath 下的文件
  • 文件系统中的文件
  • 网络 URL 资源
  • 字节数组
  • 输入流

而 JDK 本身虽然有 PathFileURLInputStream 等对象,但没有提供一个统一的资源接口把它们都抽象到一起。


2. 为什么 Spring 要提供 Resource

如果只用 JDK,处理不同来源的资源时,代码风格通常不一致:

  • 本地文件常用 Path / File
  • 网络地址常用 URL
  • 读取内容常用 InputStream

Spring 提供 Resource 后,可以统一成类似这种写法:

java
Resource resource = ...;
InputStream inputStream = resource.getInputStream();

你不一定要先关心它到底来自:

  • classpath
  • 磁盘文件
  • 远程 URL

这就是它最核心的价值。


3. 常见实现类

Spring 中最常见的几个 Resource 实现:

  • ClassPathResource:读取 classpath 资源
  • FileSystemResource:读取文件系统资源
  • UrlResource:读取 URL 资源
  • ByteArrayResource:把字节数组包装成资源
  • InputStreamResource:把输入流包装成资源

示例:

java
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 判断资源是否存在

java
Resource resource = new ClassPathResource("application.yml");
System.out.println(resource.exists());

示例结果:

text
true

4.2 获取输入流读取内容

这是最常见、最通用的方式。

java
Resource resource = new ClassPathResource("application.yml");

try (InputStream inputStream = resource.getInputStream()) {
    String text = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
    System.out.println(text);
}

示例结果:

text
server:
  port: 8080

4.3 获取文件名

java
Resource resource = new FileSystemResource("D:/data/report.txt");
System.out.println(resource.getFilename());

示例结果:

text
report.txt

4.4 获取资源描述

java
Resource resource = new ClassPathResource("application.yml");
System.out.println(resource.getDescription());

示例结果:

text
class path resource [application.yml]

4.5 获取 URL / URI / File

java
Resource resource = new FileSystemResource("D:/data/report.txt");

System.out.println(resource.getURL());
System.out.println(resource.getURI());
System.out.println(resource.getFile().getAbsolutePath());

示例结果:

text
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

java
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

java
Resource resource = new FileSystemResource("D:/data/report.txt");
File file = resource.getFile();

注意:

  • 只有资源确实对应物理文件时才适合这样做
  • 对于 jar 包内部资源,不要依赖这个方式

6.3 Resource -> Path

如果你使用的是较新的 Spring Framework 版本,可以直接拿到 Path

java
Resource resource = new FileSystemResource("D:/data/report.txt");
Path path = resource.getFilePath();

兼容旧版本时,常见写法仍然是:

java
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

java
Resource resource = new UrlResource("https://example.com/demo.txt");
URL url = resource.getURL();

6.5 File -> Resource

java
File file = new File("D:/data/report.txt");
Resource resource = new FileSystemResource(file);

6.6 Path -> Resource

java
Path path = Path.of("D:/data/report.txt");
Resource resource = new FileSystemResource(path);

6.7 URL / URI -> Resource

java
Resource r1 = new UrlResource("https://example.com/demo.txt");
Resource r2 = new UrlResource(URI.create("https://example.com/demo.txt"));

6.8 byte[] -> Resource

java
byte[] data = "hello".getBytes(StandardCharsets.UTF_8);
Resource resource = new ByteArrayResource(data);

6.9 InputStream -> Resource

java
InputStream inputStream = new ByteArrayInputStream("hello".getBytes(StandardCharsets.UTF_8));
Resource resource = new InputStreamResource(inputStream);

注意:

  • InputStreamResource 更适合“一次性消费”的流
  • 它更像“已经打开的流的包装”,不适合长期保存后再反复读取
  • 如果内容要反复读取,通常 ByteArrayResource 更稳妥

7. 和 JDK 输入输出流之间如何快速转换

7.1 Resource -> InputStream

java
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

java
InputStream in = new ByteArrayInputStream("abc".getBytes(StandardCharsets.UTF_8));
Resource resource = new InputStreamResource(in);

7.3 byte[] 作为中转:InputStream <-> Resource

如果你希望资源内容可以重复读取,最实用的方法通常是先转成字节数组。

java
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));
}

示例结果:

text
abc

7.4 Resource -> OutputStream 该怎么理解

严格说,Resource 更偏向“读资源描述”,它本身没有一个统一的 getOutputStream() 方法。

如果你想写出内容,通常用下面两种思路:

方式一:转成 WritableResource

Spring 里有些资源同时实现了 WritableResource

java
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));
}

示例结果:

text
文件 out.txt 被写入 hello

方式二:直接回到 JDK 的 Files / OutputStream

如果你拿到的是文件资源,也可以转回 JDK 再写。

java
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 配置文件

java
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

java
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));
}

示例结果:

text
upload-content

8.3 文件系统资源读写

java
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));
}

示例结果:

text
line-1

9. 使用时的注意点

9.1 不要默认所有 Resource 都能转成 File

这是最常见误区。

例如:

  • ClassPathResource 在开发环境里可能能拿到文件
  • 但项目打成 jar 后,不一定还能 getFile() 成功

所以:

读取内容优先用 getInputStream(),兼容性最好。

9.2 InputStreamResource 不适合反复读取

因为它底层就是一个输入流对象。

如果流已经消费过,再读可能失败。

而且它更适合表示一个“已经打开的资源”,不适合作为可长期保存、可重复打开的资源描述符。

若希望多次读取,优先考虑:

java
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()(新版本可用)

最常见实现

  • ClassPathResource
  • FileSystemResource
  • UrlResource
  • ByteArrayResource
  • InputStreamResource

最重要的实践经验

能用 getInputStream() 就优先用它,不要默认所有资源都能安全转成 File


11. 你可以怎么理解 PathFilesResource

最后用一句话把它们串起来:

  • Path:表示路径
  • Files:操作文件
  • Resource:统一描述资源来源

如果做的是 JDK 原生文件处理,重点通常是:

  • Path
  • Files
  • InputStream
  • OutputStream

如果做的是 Spring 项目资源处理,重点通常会变成:

  • Resource
  • InputStream
  • WritableResource
  • 需要时再回到 File / Path