Есть ли способ в Java определить, является ли путь действительным, не пытаясь создать файл?
Мне нужно определить, является ли предоставленная пользователем строка действительным путем к файлу (т. createNewFile()
Если createNewFile()
завершится успешно или сгенерирует исключение), но я не хочу разбивать файловую систему бесполезными файлами, созданными только для целей проверки.
Есть ли способ определить, является ли моя строка допустимым путем к файлу, не пытаясь создать файл?
Я знаю, что определение "допустимого пути к файлу" варьируется в зависимости от операционной системы, но мне было интересно, есть ли какой-нибудь быстрый способ принять C: /foo
или /foo
и отклонить banana
.
Возможным подходом может быть попытка создать файл и, в конечном итоге, удалить его, если создание прошло успешно, но я надеюсь, что есть более элегантный способ достижения того же результата.
Ответы
Ответ 1
Это также проверяет наличие каталога.
File file = new File("c:\\cygwin\\cygwin.bat");
if (!file.isDirectory())
file = file.getParentFile();
if (file.exists()){
...
}
Кажется, что file.canWrite() не дает вам четкого указания, если у вас есть права на запись в каталог.
Ответ 2
Класс Path, введенный в Java 7, добавляет новые альтернативы, например:
(Не работает должным образом под Linux - всегда возвращает истину)
/**
* <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;
}
Ответ 3
File.getCanonicalPath()
весьма полезен для этой цели. Исключения IO для определенных типов недопустимых имен файлов (например, CON
, PRN
, *?*
в Windows) при разрешении ОС или файловой системы. Однако это только предварительная проверка; вам все равно придется обрабатывать другие сбои при создании файла (например, недостаточные разрешения, нехватка места на диске, ограничения безопасности).
Ответ 4
При попытке создать файл может возникнуть ошибка:
- У вас нет необходимых разрешений;
- На устройстве недостаточно места;
- Устройство испытывает ошибку;
- Некоторая политика пользовательской безопасности запрещает вам создавать файл определенного типа;
- и др.
Более того, они могут меняться между тем, когда вы пытаетесь выполнить запрос, чтобы узнать, можете ли вы и когда сможете. В многопоточной среде это одна из основных причин условий гонки и может быть реальной уязвимостью некоторых программ.
В принципе, вам просто нужно попытаться создать его и посмотреть, работает ли он. И это правильный способ сделать это. Это почему ConcurrentHashMap
имеет putIfAbsent()
, поэтому проверка и вставка являются атомной операцией и не страдают от условий гонки. Точно такой же принцип здесь играет.
Если это всего лишь часть процесса диагностики или установки, просто сделайте это и посмотрите, работает ли он. Опять же нет гарантии, что он будет работать позже.
В основном ваша программа должна быть достаточно устойчивой, чтобы умело умереть, если она не может записать соответствующий файл.
Ответ 5
Здесь вы можете сделать то, что работает в разных операционных системах.
Использование соответствия регулярному выражению для проверки существующих известных недопустимых символов.
if (newName.matches(".*[/\n\r\t\0\f'?*\\<>|\":].*")) {
System.out.println("Invalid!");
} else {
System.out.println("Valid!");
}
Pros
- Это работает через операционные системы
- Вы можете настроить его так, как захотите, отредактировав это регулярное выражение.
Cons
- Это может быть не полный список, и нужно больше исследований, чтобы заполнить больше недопустимых шаблонов или символов.
Ответ 6
Просто сделай это (и убери за собой)
Возможным подходом может быть попытка создать файл и, в конечном итоге, удалить его, если создание прошло успешно, но я надеюсь, что есть более элегантный способ достижения того же результата.
Может быть, это самый надежный способ.
Ниже приводится 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 pick another destination. Maybe the OS 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 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 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
и созданием фактического файла содержимое и разрешения вашей файловой системы могли измениться.
Ответ 7
boolean canWrite(File file) {
if (file.exists()) {
return file.canWrite();
}
else {
try {
file.createNewFile();
file.delete();
return true;
}
catch (Exception e) {
return false;
}
}
}