開發時 (IDE 運行) 文件在文件系統,相對路徑 `src/main/resources/image.png` 可用。
打包成 JAR 後,資源進入 JAR 內部,**不再是文件系統文件**,`File`/`FileInputStream` 失效,**必須用 `ClassLoader` 讀取流**。
public class ResourceUtil {
/**
* 統一獲取資源輸入流 (開發環境 + JAR 包均可用)
* @param path 資源相對路徑 (如 "images/logo.png", "config/app.properties")
* @return InputStream 或 null (未找到)
*/
public static InputStream getResourceAsStream(String path) {
// 1. 優先使用線程上下文類加載器 (支持 Web 容器、OSGi、線程隔離場景)
ClassLoader cl = Thread.currentThread().getContextClassLoader();
InputStream is = cl.getResourceAsStream(path);
// 2. 回退到當前類加載器
if (is == null) {
is = ResourceUtil.class.getClassLoader().getResourceAsStream(path);
}
// 3. 最後嘗試當前類的類加載器 (相對路徑不以 / 開頭)
if (is == null) {
is = ResourceUtil.class.getResourceAsStream(path); // 相對當前類包路徑
}
return is;
}
// 便利方法:讀取 properties
public static Properties loadProperties(String path) throws IOException {
try (InputStream is = getResourceAsStream(path)) {
if (is == null) throw new FileNotFoundException("Resource not found: " + path);
Properties props = new Properties();
props.load(is);
return props;
}
}
// 便利方法:讀取圖片 (Swing/JavaFX)
public static Image loadImage(String path) {
try (InputStream is = getResourceAsStream(path)) {
if (is == null) return null;
return ImageIO.read(is); // javax.imageio.ImageIO
} catch (IOException e) {
return null;
}
}
}
| 調用方式 | 路徑格式 | 查找起點 |
|---|---|---|
| `ClassLoader.getResourceAsStream("path")` | **不以 `/` 開頭** (如 `config/db.properties`) | Classpath 根目錄 (`target/classes` / JAR 根) |
| `Class.getResourceAsStream("/path")` | **以 `/` 開頭** (如 `/config/db.properties`) | Classpath 根目錄 |
| `Class.getResourceAsStream("path")` | **不以 `/` 開頭** (如 `db.properties`) | **當前類所在包目錄** (`com/example/...`) |
src/
└── main/
├── java/
│ └── com/example/App.java
└── resources/ <-- 資源文件放這裡
├── config/
│ └── app.properties
└── images/
└── logo.png
編譯後 `target/classes/config/app.properties`,打包進 `app.jar/config/app.properties`。
代碼中統一用 `ResourceUtil.getResourceAsStream("config/app.properties")`。
@Component
public class ConfigLoader {
@Value("classpath:config/app.properties") // Spring Resource 抽象
private Resource resource;
public Properties load() throws IOException {
try (InputStream is = resource.getInputStream()) {
Properties props = new Properties();
props.load(is);
return props;
}
}
}
**`ClassLoader.getResourceAsStream("相對路徑")` 是讀取 JAR 內資源的唯一正解**。封裝工具類統一入口,資源放 `src/main/resources`,開發、測試、打包、部署全環境通用。