Files.delete和Files.deleteIfExists有点奇怪的行为

时间:2018-01-17 22:47:50

标签: java file-io delete-file nio2

我得到了这样的代码:

paths.forEach(folderPath -> {
        Path to = folderPath.getRoot().resolve(folderPath.getParent().subpath(0, folderPath.getNameCount() - 1)); // До имени (исключительно)
        try {
            Files.list(folderPath).forEach(filePath -> {
                try { Files.move(filePath, to.resolve(filePath.getFileName()), StandardCopyOption.ATOMIC_MOVE); }
                catch (IOException e) { processException(e); }
            });
            if (Files.list(folderPath).count() == 0)
                Files.deleteIfExists(folderPath); // this call
        } catch (IOException e) { processException(e); }
    });

在我调用delete方法之后,我将我的空目录锁定(在调用它之后,检查它),但在应用程序关闭之前不会删除。我觉得有点奇怪,但想知道为什么会这样。

(我使用的是Windows 10)

1 个答案:

答案 0 :(得分:0)

来自Files.list(Path)的文档:

  

必须在try-with-resources语句或类似的控制结构中使用此方法,以确保在流的操作完成后立即关闭流的打开目录。

您没有这样做,因此Files.deleteIfExists(…)的以下部分适用:

  

在某些操作系统上,当该文件在此Java虚拟机或其他程序中打开并正在使用时,可能无法删除该文件。

你应该使用

paths.forEach(folderPath -> {
    Path to = folderPath.getParent();
    try {
        try(Stream<Path> files = Files.list(folderPath)) {
            files.forEach(filePath -> {
                try{Files.move(filePath, to.resolve(filePath.getFileName()), ATOMIC_MOVE);}
                catch (IOException e) { processException(e); }
            });
        }
        try {
            Files.deleteIfExists(folderPath);
        } catch(DirectoryNotEmptyException ex) {
            // may happen as you continue when Files.move fails,
            // but you already reported the original exception then
        }
    } catch (IOException e) { processException(e); }
});

这会在尝试删除目录之前关闭文件流。请注意,第二个流操作已被删除,这种预检是浪费的,并且在所有move操作成功时都应该不需要。但如果某些其他应用程序同时插入新文件,则无法保证在Files.list(folderPath).count() == 0检查和后续deleteIfExists调用之间不会发生这种情况。

更清洁的解决方案是记住move失败的时间。如果没有move失败,则应将仍然不为空的目录视为错误情况,应该像任何其他错误一样进行报告,例如

paths.forEach(folderPath -> {
    Path to = folderPath.getParent();
    try {
        boolean allMovesSucceeded;
        try(Stream<Path> files = Files.list(folderPath)) {
          allMovesSucceeded = files
            .map(filePath -> {
                try {
                    Files.move(filePath, to.resolve(filePath.getFileName()), ATOMIC_MOVE);
                    return true;
                }
                catch(IOException e) { processException(e); return false; }
            }).reduce(Boolean.TRUE, Boolean::logicalAnd);

        }
        if(allMovesSucceeded) Files.deleteIfExists(folderPath);
    } catch (IOException e) { processException(e); }
});