在 Java 開發中,目錄複製是常見的文件操作需求。特別是當目錄結構較深、包含多層子目錄與文件時,如何高效且正確地實現「連帶子目錄與文件一起複製」是許多開發者關注的焦點。本文將通過一個經典的遞歸實現方案,詳細拆解核心邏輯、關鍵 API 使用要點,以及實際開發中需注意的邊界情況。
目錄複製的本質是「遍歷源目錄樹,並在目標路徑重建相同結構」。採用深度優先遞歸策略:
/**
* 遞歸複製目錄及其所有子項到目標路徑
* @param src 源目錄/文件 File 對象
* @param dest 目標目錄/文件 File 對象
* @throws IOException IO 異常
*/
private void copyFolder(File src, File dest) throws IOException {
if (src.isDirectory()) {
// 1. 目標目錄不存在則創建
if (!dest.exists()) {
dest.mkdir(); // 僅創建單級目錄,父目錄需已存在
}
// 2. 遍歷源目錄下所有子項
String[] files = src.list();
for (String file : files) {
File srcFile = new File(src, file);
File destFile = new File(dest, file);
// 3. 遞歸調用處理子項
copyFolder(srcFile, destFile);
}
} else {
// 4. 文件複製:字節流讀寫
try (InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dest)) {
byte[] buffer = new byte[1024];
int length;
while ((length = in.read(buffer)) > 0) {
out.write(buffer, 0, length);
}
} // try-with-resources 自動關閉流
}
}
isDirectory():判斷是否為目錄list():獲取目錄下所有子項名稱數組(不包含路徑)mkdir() vs mkdirs():前者僅創建單級目錄,後者可創建多級目錄。本例中因遞歸特性,父目錄已存在,用 mkdir() 即可new File(parent, child):構建子路徑 File 對象,自動處理路徑分隔符採用 1KB 緩衝區(byte[1024])分塊讀寫,避免大文件一次性讀入內存導致 OOM。in.read(buffer) 返回實際讀取字節數,-1 表示讀取結束。
代碼使用 try-with-resources 語法,確保 InputStream 和 OutputStream 在正常返回或拋出異常時都能自動關閉,避免資源洩漏。這是現代 Java 開發的推薦做法。
| 問題場景 | 原因分析 | 解決方案 |
|---|---|---|
| 目標目錄父路徑不存在 | mkdir() 僅創建最後一級 | 改用 dest.mkdirs() 或確保調用前父目錄已存在 |
| 源目錄為空 | list() 返回空數組 | 循環不執行,目標創建空目錄,符合預期 |
| 大文件複製性能 | 1KB 緩衝區較小 | 可增大至 8KB-64KB,或使用 NIO FileChannel.transferTo() 實現零拷貝 |
| 符號鏈接處理 | 遞歸會跟隨鏈接進入目標 | 若需保留鏈接,需額外判斷 Files.isSymbolicLink() |
Java 7 引入的 java.nio.file 包提供更強大的文件操作 API,複製目錄可簡化為:
Path source = Paths.get("/path/to/src");
Path target = Paths.get("/path/to/dest");
Files.walkFileTree(source, new SimpleFileVisitor() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
Path targetDir = target.resolve(source.relativize(dir));
Files.createDirectories(targetDir);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.copy(file, target.resolve(source.relativize(file)), StandardCopyOption.REPLACE_EXISTING);
return FileVisitResult.CONTINUE;
}
});
此方案自動處理路徑解析、異常傳播、屬性保留等細節,生產環境推薦使用。
目錄遞歸複製的核心在於「分治」思想:將複雜的樹結構操作分解為「目錄創建」與「文件複製」兩類原子操作,通過遞歸實現全樹遍歷。理解這一邏輯不僅能解決當前問題,更能舉一反三應用於目錄刪除、同步備份、結構對比等場景。實際項目中建議優先考慮 NIO.2 方案,以獲得更好的異常處理、性能與代碼可維護性。