1. 简介

在 Java 中复制目录(包含所有文件和子目录)是一个常见但容易踩坑的操作。实现方式主要有三种:使用 JDK 自带的 NIO API、传统的 IO 流 + 递归,或者借助第三方工具类库如 Apache Commons IO。

本文将逐一演示这些方法,帮你选出最适合当前项目的方案。✅


2. 使用 java.nio API(推荐)

从 Java 7 开始,java.nio.file 包(也就是常说的 NIO.2)大大简化了文件操作。它提供了 FilesPathsPath 等核心类,让目录遍历和复制变得简单粗暴。

核心思路:

  • 使用 Files.walk() 遍历源目录下的所有路径(包括子目录和文件)
  • 对每个路径,计算其相对于源目录的相对路径,拼接到目标目录
  • 调用 Files.copy() 完成复制

示例代码:

public static void copyDirectory(String sourceDirectoryLocation, String destinationDirectoryLocation) 
    throws IOException {
    Files.walk(Paths.get(sourceDirectoryLocation))
        .forEach(source -> {
            Path destination = Paths.get(destinationDirectoryLocation, 
                source.toString().substring(sourceDirectoryLocation.length()));
            try {
                Files.copy(source, destination);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
}

⚠️ 注意事项:

  • Files.walk() 默认深度优先遍历,能正确处理嵌套目录
  • 如果目标路径已存在文件,Files.copy() 会抛出异常(可通过 StandardCopyOption.REPLACE_EXISTING 控制)
  • 此方法依赖 Java 7+,不适用于老版本

✅ 优点:代码简洁、性能好、支持符号链接和文件属性
❌ 缺点:Java 7 以下不可用


3. 使用 java.io API(兼容旧版本)

如果你还在维护 Java 6 或更早项目,就得靠 java.io.File + 手动递归实现。

实现逻辑:

  1. 检查目标目录是否存在,不存在则创建
  2. 遍历源目录下的所有条目
  3. 如果是目录,递归调用自身
  4. 如果是文件,使用 FileInputStreamFileOutputStream 复制内容

目录复制主方法:

private static void copyDirectory(File sourceDirectory, File destinationDirectory) throws IOException {
    if (!destinationDirectory.exists()) {
        destinationDirectory.mkdir();
    }
    for (String f : sourceDirectory.list()) {
        copyDirectoryCompatibityMode(new File(sourceDirectory, f), new File(destinationDirectory, f));
    }
}

分发处理方法:

public static void copyDirectoryCompatibityMode(File source, File destination) throws IOException {
    if (source.isDirectory()) {
        copyDirectory(source, destination);
    } else {
        copyFile(source, destination);
    }
}

文件复制实现:

private static void copyFile(File sourceFile, File destinationFile) 
    throws IOException {
    try (InputStream in = new FileInputStream(sourceFile); 
         OutputStream out = new FileOutputStream(destinationFile)) {
        byte[] buf = new byte[1024];
        int length;
        while ((length = in.read(buf)) > 0) {
            out.write(buf, 0, length);
        }
    }
}

⚠️ 踩坑提醒:

  • 必须手动创建中间目录(mkdir() 不会创建父目录,建议改用 mkdirs()
  • source.list() 返回的是文件名数组,不包含路径信息
  • 建议使用 try-with-resources 确保流正确关闭

✅ 优点:兼容性强,Java 1.4+ 可用
❌ 缺点:代码冗长,易出错,性能一般


4. 使用 Apache Commons IO(最简单)

对于大多数现代 Java 项目,直接使用 Apache Commons IO 是最省事的选择。

添加依赖:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.15.1</version>
</dependency>

💡 注意:Maven 坐标已更新为最新常用版本,避免使用过时的 commons-io:commons-io

一行代码完成复制:

public static void copyDirectory(String sourceDirectoryLocation, String destinationDirectoryLocation) throws IOException {
    File sourceDirectory = new File(sourceDirectoryLocation);
    File destinationDirectory = new File(destinationDirectoryLocation);
    FileUtils.copyDirectory(sourceDirectory, destinationDirectory);
}

✨ 你没看错,就这么一行核心代码。

其他实用方法:

  • FileUtils.copyDirectoryToDirectory():复制目录到另一个目录下
  • FileUtils.copyFile():复制单个文件
  • FileUtils.deleteDirectory():安全删除整个目录

✅ 优点:代码极简、健壮、经过广泛测试
❌ 缺点:引入额外依赖,对轻量项目可能略重


5. 总结

方法 Java 版本要求 是否推荐 适用场景
java.nio + Files.walk() Java 7+ ✅ 强烈推荐 现代项目,无外部依赖要求
java.io + 递归 Java 1.4+ ⚠️ 仅限兼容需求 维护老系统
Apache Commons IO Java 1.6+ ✅ 推荐 追求开发效率,已有该依赖

📌 最佳实践建议:

  • 新项目优先使用 NIO 方式
  • 若已引入 Commons IO,直接调用 FileUtils.copyDirectory() 更省心
  • 生产环境务必处理异常,避免静默失败

完整示例代码已托管至 GitHub:https://github.com/baeldung/core-java-modules/tree/master/core-java-io-3


原始标题:Copy a Directory in Java