在 Java 開發中,正確取得檔案路徑是常見痛點:IDE 執行、JAR 打包部署、單元測試、Web 容器等環境下路徑表現各異。本文系統整理六種常見路徑取得場景,提供標準、可攜的最佳實踐。
| 需求場景 | 推薦 API | 回傳型別 | 適用環境 |
|---|---|---|---|
| 目前類別所在目錄 (編譯後 .class 位置) | ThisClass.class.getResource("").getPath() | String | 所有 |
| Classpath 根目錄 (src/main/resources 編譯後) | ThisClass.class.getResource("/").getPath() | String | 所有 |
| 專案根目錄 (Maven/Gradle 專案根目錄) | new File("").getCanonicalPath() 或 System.getProperty("user.dir") | String | IDE、命令列、測試 |
| 讀取 Classpath 下資源檔 (如 properties、xml) | ThisClass.class.getClassLoader().getResourceAsStream("file.name") | InputStream | 所有 (含 JAR 內) |
| 取得資源檔 URL (可轉 File) | ThisClass.class.getClassLoader().getResource("file.name") | URL | 所有 |
| Web 專案實體部署路徑 (ServletContext) | servletContext.getRealPath("/") | String | 僅 Web 容器 |
package com.example.path;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Properties;
import org.junit.Test;
public class PathDemoTest {
/** 1. 取得類別編譯輸出目錄 (target/classes/.../com/example/path) */
@Test
public void getClassOutputDir() {
File f = new File(this.getClass().getResource("").getPath());
System.out.println("類別目錄: " + f.getAbsolutePath());
// 輸出範例: /project/target/test-classes/com/example/path
}
/** 2. 取得 Classpath 根目錄 (target/classes 或 target/test-classes) */
@Test
public void getClasspathRoot() {
File f = new File(this.getClass().getResource("/").getPath());
System.out.println("Classpath 根目錄: " + f.getAbsolutePath());
// 輸出範例: /project/target/test-classes
}
/** 3. 取得專案根目錄 (Maven 專案根目錄) - 兩種等效寫法 */
@Test
public void getProjectRoot() throws IOException {
File f = new File("");
String canonical = f.getCanonicalPath();
String userDir = System.getProperty("user.dir");
System.out.println("File canonical: " + canonical);
System.out.println("user.dir: " + userDir);
// 兩者通常相同: /project
}
/** 4. 讀取 src/main/resources 下的資源檔 (推薦用 InputStream) */
@Test
public void readResourceFile() {
// 讀取 jdbc.properties
try (InputStream is = this.getClass().getClassLoader().getResourceAsStream("jdbc.properties")) {
Properties props = new Properties();
props.load(is);
System.out.println("driver: " + props.getProperty("jdbc.driverClassName"));
} catch (Exception e) {
e.printStackTrace();
}
}
/** 5. 取得其他 package 下的資源檔 URL */
@Test
public void getOtherPackageResource() {
// 讀取同層級 test2.txt
URL url = this.getClass().getClassLoader().getResource("test2.txt");
System.out.println("資源 URL: " + url);
// file:/project/target/classes/test2.txt
}
/** 6. URL 轉 File 並讀取內容 (注意: JAR 內資源無法轉 File) */
@Test
public void urlToFile() throws Exception {
URL url = this.getClass().getClassLoader().getResource("test2.txt");
if (url != null && "file".equals(url.getProtocol())) {
File file = new File(url.toURI()); // 使用 toURI() 避免空格編碼問題
System.out.println("檔案絕對路徑: " + file.getAbsolutePath());
}
}
}
getResource(""):相對路徑,**目前類別所在套件目錄**。getResource("/"):絕對路徑,**Classpath 根目錄**。jar:file:/path/to.jar!/com/example/ 格式 URL,無法直接轉 File。// ✅ 正確: 委派給系統類別載入器,可跨模組、JAR 讀取
InputStream is = this.getClass().getClassLoader().getResourceAsStream("config.properties");
// ❌ 錯誤: 僅搜尋目前類別所在套件路徑
InputStream is = this.getClass().getResourceAsStream("config.properties");
// ❌ 直接 getPath() 會保留 %20 等編碼 String path = url.getPath(); // ✅ 使用 toURI() 再轉 File,自動處理編碼 File file = new File(url.toURI());
@Autowired
private ResourceLoader resourceLoader;
// 讀取 classpath:config.yml
Resource resource = resourceLoader.getResource("classpath:config.yml");
try (InputStream is = resource.getInputStream()) {
// 處理流
}
@Controller
public class MyController {
@Autowired
private ServletContext servletContext;
public void printWebRoot() {
// 僅適用於解壓縮部署 (exploded war),壓縮 WAR 回傳 null
String realPath = servletContext.getRealPath("/");
System.out.println("Web 根目錄: " + realPath);
}
}
| 環境 | user.dir | Classpath 根目錄 | 資源檔讀取 |
|---|---|---|---|
| IDE 執行 (IntelliJ/Eclipse) | 專案根目錄 | target/classes | ✅ InputStream 正常 |
| Maven 測試 | 專案根目錄 | target/test-classes | ✅ InputStream 正常 |
| 打包 JAR 執行 | JAR 所在目錄 | JAR 內部根目錄 | ✅ InputStream 正常 (不可轉 File) |
| Spring Boot 執行 | 啟動命令目錄 | BOOT-INF/classes | ✅ ResourceLoader 建議 |
掌握「Classpath 資源流讀取」與「專案工作目錄」的差異,是 Java 開發者處理配置檔、模板、靜態資源的核心功力。**原則:唯讀資源統一用 getResourceAsStream();需寫入或掃描目錄才用 File 操作專案目錄。**