Files.delete 和 Files.deleteIfExists 有点奇怪的行为
A bit strange behaviour of Files.delete and Files.deleteIfExists
我得到这样的代码:
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); }
});
调用删除方法后,我锁定了我的空目录(调用后立即检查),但直到应用程序关闭后才删除。我觉得有点奇怪,但想知道为什么会这样。
(我用Windows10)
来自 Files.list(Path)
的文档:
This method must be used within a try-with-resources statement or similar control structure to ensure that the stream's open directory is closed promptly after the stream's operations have completed.
您没有这样做,因此 Files.deleteIfExists(…) 的以下部分适用:
On some operating systems it may not be possible to remove a file when it is open and in use by this Java virtual machine or other programs.
你应该使用
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); }
});
我得到这样的代码:
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); }
});
调用删除方法后,我锁定了我的空目录(调用后立即检查),但直到应用程序关闭后才删除。我觉得有点奇怪,但想知道为什么会这样。
(我用Windows10)
来自 Files.list(Path)
的文档:
This method must be used within a try-with-resources statement or similar control structure to ensure that the stream's open directory is closed promptly after the stream's operations have completed.
您没有这样做,因此 Files.deleteIfExists(…) 的以下部分适用:
On some operating systems it may not be possible to remove a file when it is open and in use by this Java virtual machine or other programs.
你应该使用
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); }
});