我需要确定用户提供的字符串是否是有效的文件路径(即,如果 createNewFile()
成功或抛出异常),但我不想使用仅出于验证目的而创建的无用文件来膨胀文件系统。
有没有一种方法可以确定我拥有的字符串是否是有效的文件路径,而无需尝试创建文件?
我知道“有效文件路径”的定义因操作系统而异,但是我想知道是否有任何快速的接受C:/foo
或/foo
拒绝方法banana
。
一种可能的方法是尝试创建文件,如果创建成功,则最终将其删除,但我希望有一种更优雅的方法来实现相同的结果。
Answers:
这也将检查目录的存在。
File file = new File("c:\\cygwin\\cygwin.bat");
if (!file.isDirectory())
file = file.getParentFile();
if (file.exists()){
...
}
似乎file.canWrite()并没有明确指示您是否具有写入目录的权限。
new File("some/path")
。因此,您可以将它们用于验证目的。要创建文件系统上的文件将不得不createNewFile
等等
在Java 7中引入了Path类增加了新的选择,如下所示:
(不不是下正常工作的Linux -总是返回true)
/**
* <pre>
* Checks if a string is a valid path.
* Null safe.
*
* Calling examples:
* isValidPath("c:/test"); //returns true
* isValidPath("c:/te:t"); //returns false
* isValidPath("c:/te?t"); //returns false
* isValidPath("c/te*t"); //returns false
* isValidPath("good.txt"); //returns true
* isValidPath("not|good.txt"); //returns false
* isValidPath("not:good.txt"); //returns false
* </pre>
*/
public static boolean isValidPath(String path) {
try {
Paths.get(path);
} catch (InvalidPathException | NullPointerException ex) {
return false;
}
return true;
}
true
在linux上返回。在我自己的测试中,甚至Paths.get("file\u0000")
没有抛出InvalidPathException
。
File.getCanonicalPath()
为此非常有用。IO抛出异常某些类型的无效文件名(例如CON
,PRN
,*?*
在Windows中)针对OS或文件系统解析时。但是,这仅是初步检查;在实际创建文件时,您仍然需要处理其他失败(例如,权限不足,驱动器空间不足,安全限制)。
尝试创建文件时,很多事情都会出错:
更重要的是,这些内容可能会在您尝试查询是否可行以及何时实际可行之间进行更改。在多线程环境中,这是争用条件的主要原因之一,并且可能是某些程序的真正漏洞。
基本上,您只需要尝试创建它,看看它是否有效。这是正确的方法。这就是为什么诸如此类的东西ConcurrentHashMap
具有一个putIfAbsent()
check和insert是一个原子操作并且不受竞争条件影响的原因。完全相同的原理在这里起作用。
如果这只是某些诊断或安装过程的一部分,请执行此操作并查看其是否有效。同样,不能保证它将在以后工作。
基本上,您的程序必须足够强大,才能在无法编写相关文件的情况下正常退出。
一种可能的方法是尝试创建文件,如果创建成功,则最终将其删除,但我希望有一种更优雅的方法来实现相同的结果。
也许这是最可靠的方法。
以下是canCreateOrIsWritable
确定您的程序是否能够创建文件及其父目录的信息在给定路径下,或者,如果那里已有文件,则对其进行写入。
通过实际创建必要的父目录以及路径中的空文件来实现。之后,它将删除它们(如果路径中存在文件,则将其保留)。
使用方法如下:
var myFile = new File("/home/me/maybe/write/here.log")
if (canCreateOrIsWritable(myFile)) {
// We're good. Create the file or append to it
createParents(myFile);
appendOrCreate(myFile, "new content");
} else {
// Let's pick another destination. Maybe the OS's temporary directory:
var tempDir = System.getProperty("java.io.tmpdir");
var alternative = Paths.get(tempDir, "second_choice.log");
appendOrCreate(alternative, "new content in temporary directory");
}
基本方法和一些辅助方法:
static boolean canCreateOrIsWritable(File file) {
boolean canCreateOrIsWritable;
// The non-existent ancestor directories of the file.
// The file's parent directory is first
List<File> parentDirsToCreate = getParentDirsToCreate(file);
// Create the parent directories that don't exist, starting with the one
// highest up in the file system hierarchy (closest to root, farthest
// away from the file)
reverse(parentDirsToCreate).forEach(File::mkdir);
try {
boolean wasCreated = file.createNewFile();
if (wasCreated) {
canCreateOrIsWritable = true;
// Remove the file and its parent dirs that didn't exist before
file.delete();
parentDirsToCreate.forEach(File::delete);
} else {
// There was already a file at the path → Let's see if we can
// write to it
canCreateOrIsWritable = java.nio.file.Files.isWritable(file.toPath());
}
} catch (IOException e) {
// File creation failed
canCreateOrIsWritable = false;
}
return canCreateOrIsWritable;
}
static List<File> getParentDirsToCreate(File file) {
var parentsToCreate = new ArrayList<File>();
File parent = file.getParentFile();
while (parent != null && !parent.exists()) {
parentsToCreate.add(parent);
parent = parent.getParentFile();
}
return parentsToCreate;
}
static <T> List<T> reverse(List<T> input) {
var reversed = new ArrayList<T>();
for (int i = input.size() - 1; i >= 0; i--) {
reversed.add(input.get(i));
}
return reversed;
}
static void createParents(File file) {
File parent = file.getParentFile();
if (parent != null) {
parent.mkdirs();
}
}
请记住,在调用canCreateOrIsWritable
和创建实际文件之间,文件系统的内容和权限可能已更改。