將源目錄 f:/Exercises 遞歸複製到目標目錄 f:/book,保持子目錄層級與文件內容完全一致。
public static void copyFile(File src, File dest) {
if (src.isDirectory()) {
// 1. 創建目標目錄
dest.mkdir(); // 若父目錄不存在會失敗,生產建議 mkdirs()
// 2. 遍歷子項遞歸
File[] children = src.listFiles();
if (children != null) {
for (File child : children) {
copyFile(child, new File(dest, child.getName()));
}
}
} else if (src.isFile()) {
// 3. 文件拷貝:創建目標文件 + 字節流拷貝內容
try {
dest.createNewFile(); // 確保文件存在
copyData(src.getAbsolutePath(), dest.getAbsolutePath());
} catch (IOException e) { e.printStackTrace(); }
}
}
copyData()public static void copyData(String srcPath, String destPath) {
try (FileInputStream fis = new FileInputStream(srcPath);
FileOutputStream fos = new FileOutputStream(destPath)) {
byte[] buffer = new byte[1024]; // 1KB 緩衝區
int len;
while ((len = fis.read(buffer)) != -1) { // 讀取到緩衝區
fos.write(buffer, 0, len); // 寫入實際讀取長度
}
} catch (IOException e) { e.printStackTrace(); }
}
關鍵點:
try-with-resources 自動關閉流,無需 finallyfis.read(buffer) 返回實際讀取字節數,-1 表示 EOFfos.write(buffer, 0, len) 僅寫入有效數據,避免殘留垃圾字節public class DirectoryCopier {
public static void main(String[] args) {
File src = new File("f:/Exercises");
File dest = new File("f:/book");
if (!src.exists()) {
System.err.println("源目錄不存在: " + src.getAbsolutePath());
return;
}
long start = System.currentTimeMillis();
copyFile(src, dest);
System.out.printf("拷貝完成,耗時 %d ms%n", System.currentTimeMillis() - start);
}
// copyFile() 與 copyData() 方法放此處
}
| 缺陷 | 增強方案 |
|---|---|
mkdir() 父目錄不存在失敗 | 改用 dest.mkdirs() 遞歸創建 |
| 無進度反饋 | 引入 ProgressMonitor 或回調接口報告已拷貝字節/文件數 |
| 大文件內存壓力 | 緩衝區調大至 64KB-256KB;或用 FileChannel.transferTo() 零拷貝 |
| 符號鏈接/特殊文件 | Files.walkFileTree() (NIO.2) 可精確控制訪問選項 |
| 權限保留 | NIO.2 Files.copy(src, dest, StandardCopyOption.COPY_ATTRIBUTES) |
| 併發拷貝 | 目錄級並行:ForkJoinPool 提交子目錄任務 |
| 異常處理粗糙 | 自定義 CopyException 封裝路徑、原因,上層統一處理 |
Files.walkFileTree(src.toPath(), new SimpleFileVisitor() { @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { Path target = dest.toPath().resolve(src.toPath().relativize(dir)); Files.createDirectories(target); return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { Path target = dest.toPath().resolve(src.toPath().relativize(file)); Files.copy(file, target, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES); return FileVisitResult.CONTINUE; } });
優勢: 自動處理符號鏈接、權限、原子性替換、異常傳播,代碼更健壯。
遞歸 + 字節流是理解文件系統操作的基礎範式。掌握 File.listFiles()、mkdirs()、createNewFile()、緩衝流讀寫循環,能應對絕大多數文件拷貝場景。生產環境強烈建議升級至 NIO.2 Files.walkFileTree() 或成熟工具庫,獲得更強的健壯性與性能。