You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows环境下Apache Commons FileUtils拷贝目录是否锁原文件?

Apache Commons FileUtils.copyDirectoryToDirectory() 在Windows下的文件锁定问题

该方法会锁定原文件吗?

答案是会。在Windows平台上,当这个方法拷贝单个文件时,会暂时锁定原文件,导致你的WRITER程序无法写入。

具体原因是:FileUtils默认用FileInputStream读取源文件,而Windows系统中,FileInputStream打开文件时采用的共享模式是FILE_SHARE_READ——简单说就是,其他进程可以读这个文件,但没法写、改或者删,直到这个输入流关闭(也就是当前文件拷贝完成)。如果WRITER刚好在某个文件拷贝期间尝试写入,大概率会遇到“文件已被占用”的错误。

怎么避免这个锁定问题?

要绕过这个限制,你需要自定义文件拷贝逻辑,让读取源文件时允许其他进程同时写入。这里有两个实用方案:

方案1:用Java NIO的FileChannel设置共享写权限

Java 7及以上版本支持针对Windows的扩展选项ExtendedOpenOption.FILE_SHARE_WRITE,可以在读取文件时明确允许其他进程写入。你可以写一个自定义的目录拷贝方法,替代FileUtils的默认实现:

import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.*;

public class SafeDirectoryCopier {
    public static void copyDirectoryWithSharedAccess(Path sourceDir, Path targetDir) throws IOException {
        // 遍历源目录下的所有文件和子目录
        Files.walk(sourceDir)
             .forEach(sourcePath -> {
                 Path targetPath = targetDir.resolve(sourceDir.relativize(sourcePath));
                 try {
                     if (Files.isDirectory(sourcePath)) {
                         // 创建对应的目标目录
                         Files.createDirectories(targetPath);
                     } else {
                         // 打开源文件:允许共享读、共享写
                         try (FileChannel sourceChannel = FileChannel.open(
                                 sourcePath,
                                 StandardOpenOption.READ,
                                 ExtendedOpenOption.FILE_SHARE_READ,
                                 ExtendedOpenOption.FILE_SHARE_WRITE);
                              // 打开目标文件:创建、写入、覆盖原有内容
                              FileChannel targetChannel = FileChannel.open(
                                 targetPath,
                                 StandardOpenOption.CREATE,
                                 StandardOpenOption.WRITE,
                                 StandardOpenOption.TRUNCATE_EXISTING)) {
                             // 高效传输文件内容
                             sourceChannel.transferTo(0, sourceChannel.size(), targetChannel);
                         }
                     }
                 } catch (IOException e) {
                     throw new RuntimeException("拷贝文件失败: " + sourcePath, e);
                 }
             });
    }
}

这个方法在读取源文件时不会阻止WRITER程序写入,完美解决锁定问题。

方案2:扩展FileUtils,自定义文件拷贝逻辑

如果你想继续用FileUtils的目录遍历、权限处理等现成功能,可以重写它的文件拷贝方法,用RandomAccessFile来读取源文件——它在Windows下的只读模式默认允许其他进程读写:

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;

public class SharedAccessFileUtils extends FileUtils {
    public static void copyFileWithSharedRead(File source, File destination) throws IOException {
        // 用RandomAccessFile打开源文件(只读模式)和目标文件(读写模式)
        try (RandomAccessFile rafSource = new RandomAccessFile(source, "r");
             RandomAccessFile rafDest = new RandomAccessFile(destination, "rw")) {
            // 用IOUtils拷贝流内容
            IOUtils.copy(rafSource.getInputStream(), rafDest.getOutputStream());
        }
    }
}

之后你可以遍历源目录的所有文件,调用这个自定义的copyFileWithSharedRead方法,完成整个目录的拷贝。

额外提醒

  • 这些方案主要针对Windows系统,Linux等类Unix系统默认读取文件不会阻止写入,所以不需要额外处理。
  • 允许共享写后,拷贝过程中可能会读到WRITER程序正在写入的部分内容,如果需要保证拷贝文件的完整性,可能需要额外的同步机制——比如让WRITER先写入临时文件,完成后再重命名到目标位置,或者用文件锁来协调拷贝和写入操作。

内容的提问来源于stack exchange,提问作者Flatfoot

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:49:33