删除文件夹后出现 AccessDeniedException

AccessDeniedException after folder deleted

I 运行 测试用例并将执行期间创建的文件保存在 'execution' 目录中(来自每个测试的文件在单独的子目录中) 在套件开始时我想清理这个文件夹

所以在 @BeforeSuite 方法中我读取了 'execution' 目录中的所有项目:

public static List<File> getRecursivelyAllFilesFromDirectory(String directory) throws IOException {
    List<File> files = new ArrayList<>();
    files.addAll(readAllFilesFromDirectory(directory));

    List<File> subDirs = readSubdirectories(directory);

    subDirs.forEach(dir -> {
        try {
            files.addAll(readAllFilesFromDirectory(dir.getPath()));
            files.add(dir);
        } catch (IOException e) {
            e.printStackTrace();
        }
    });

    return files;
}

然后我想删除所有内容

public static void cleanExecutionDirectory() throws IOException {
    LOGGER.info("Cleaning execution directory...");
    String executionDir = getExecutionDir();
    List<File> files = FileUtils.getRecursivelyAllFilesFromDirectory(executionDir);
    for (File file : files) {
        file.delete();
    }
    LOGGER.info(String.format("%d items deleted", files.size()));
}

但接下来在 @BeforeScenario 中,我尝试为新执行创建具有相同名称的子文件夹

public static void setTestCaseDir(String testCaseName) throws IOException {
    testCaseDir = testCaseName.split(StringUtils.SPACE)[0];
    String executionDir = getExecutionDir();
    Path testCaseDirPath = Paths.get(executionDir, testCaseDir);
    boolean isSubDir = readSubdirectories(executionDir)
            .stream()
            .filter(subDir -> subDir.getPath().equals(testCaseDirPath))
            .findFirst()
            .isPresent();
    if (!isSubDir) {
        Files.createDirectory(testCaseDirPath);
    }
    testCaseDir = testCaseDirPath.toString();
}

我收到一个异常:

java.nio.file.AccessDeniedException: D:\...\src\test\resources\execution\TC01

at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:83)
at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97)
at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102)
at sun.nio.fs.WindowsFileSystemProvider.createDirectory(WindowsFileSystemProvider.java:504)
at java.nio.file.Files.createDirectory(Files.java:674)
at utils.ExecutionUtils.setTestCaseDir(ExecutionUtils.java:56)
at integration.steps.StepDefs.beforeScenario(StepDefs.java:29)

尽管 setTestCaseDir 方法中的 readSubdirectories() returns 空列表也会发生。 我在文件资源管理器中看到这个子目录是可见的,但我也无法访问它们。 当此执行由于此异常而失败时,此子文件夹将解锁并消失。

什么进程可能锁定了这个子文件夹以及如何解决这个问题?

请注意,对层次结构中文件的任何延迟未关闭操作都会阻止删除工作 - 因此请检查在这些目录上工作的其他测试并为目录流添加 try-with-resources (Files list / newDirectoryStream) 和 File Input/Output 流(还有 Windows 上的 MappedByteBuffer)。

一旦修复,FileVisitorFiles.walkFileTree 提供了一种更可靠的删除树的方法,它比大型目录树上的递归 File.list 更快:

public static void main(String[] args) throws IOException {
    var paths = Arrays.stream(args).map(Path::of).collect(Collectors.toList());

    FileVisitor<Path> deleteTree = new FileVisitor<>() {
        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException  {
            return FileVisitResult.CONTINUE;
        }
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
            System.out.println("Delete FILE "+file);
            Files.delete(file);
            return FileVisitResult.CONTINUE;
        }
        public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
            throw exc;
        }
        public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
            System.out.println("Delete DIR "+dir);
            Files.delete(dir);
            return FileVisitResult.CONTINUE;
        }
    };

    for (Path p : paths) {
        Files.walkFileTree(p, deleteTree);
    }
}

受@DuncG 的启发,我使用 NIO2 和 Java8

解决了这个问题
public static void cleanExecutionDirectory() throws IOException {
    LOGGER.info("Cleaning execution directory...");
    File executionDir = new File(getExecutionDir());
    if (executionDir.exists()) {
        Path executionDirPath = Paths.get(getExecutionDir());
        Files.walk(executionDirPath)
                .sorted(Comparator.reverseOrder())
                .forEach(path -> {
                    try {
                        Files.delete(path);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });
        LOGGER.info(String.format("Items deleted"));
    }
    executionDir.mkdir();
}