Есть ли способ в Java, чтобы определить, является ли путь действительным, не пытаясь создать файл? - PullRequest
58 голосов
/ 22 января 2009

Мне нужно определить, является ли введенная пользователем строка действительным путем к файлу (т. Е. Если createNewFile() удастся или выдаст исключение), но я не хочу разбивать файловую систему ненужными файлами, созданными только для Цели проверки.

Есть ли способ определить, является ли моя строка допустимым путем к файлу, не пытаясь создать файл?

Я знаю, что определение "допустимого пути к файлу" варьируется в зависимости от ОС, но мне было интересно, есть ли какой-нибудь быстрый способ принять C:/foo или /foo и отклонить banana.

Возможным подходом может быть попытка создать файл и в конечном итоге удалить его, если создание завершилось успешно, но я надеюсь, что есть более элегантный способ достижения того же результата.

Ответы [ 7 ]

28 голосов
/ 22 января 2009

Это также проверит наличие каталога.

File file = new File("c:\\cygwin\\cygwin.bat");
if (!file.isDirectory())
   file = file.getParentFile();
if (file.exists()){
    ...
}

Похоже, file.canWrite () не дает четкого указания, если у вас есть права на запись в каталог.

26 голосов
/ 17 февраля 2016

Класс Path, введенный в Java 7, добавляет новые альтернативы, такие как следующие:
( не работает правильно при Linux - всегда возвращает true)

<code>/**
 * <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
 * 
* / public static boolean isValidPath (String path) { пытаться { Paths.get (путь); } catch (InvalidPathException | NullPointerException ex) { вернуть ложь; } вернуть истину; }
16 голосов
/ 22 января 2009

File.getCanonicalPath() весьма полезен для этой цели. Исключения ввода-вывода вызываются для определенных типов недопустимых имен файлов (например, CON, PRN, *?* в Windows) при разрешении в ОС или файловой системе. Однако это только предварительная проверка; вам все равно придется обрабатывать другие сбои при создании файла (например, недостаточные разрешения, нехватка места на диске, ограничения безопасности).

15 голосов
/ 22 января 2009

При попытке создать файл может произойти ряд ошибок:

  • Ваше отсутствие необходимых разрешений;
  • На устройстве недостаточно места;
  • В устройстве произошла ошибка;
  • Некоторые политики пользовательской безопасности запрещают создание файла определенного типа;
  • и т.д.

Более того, они могут меняться между тем, когда вы пытаетесь запросить, чтобы увидеть, если вы можете, и когда вы действительно можете. В многопоточной среде это является одной из основных причин состояния гонки и может стать реальной уязвимостью некоторых программ.

По сути, вам просто нужно попытаться создать его и посмотреть, работает ли он. И это правильный способ сделать это. Вот почему такие вещи, как ConcurrentHashMap имеют putIfAbsent(), поэтому проверка и вставка являются атомарной операцией и не страдают от условий гонки. Здесь точно такой же принцип.

Если это просто часть какого-либо процесса диагностики или установки, просто сделайте это и посмотрите, работает ли он. Опять же, нет гарантии, что это сработает позже.

Обычно ваша программа должна быть достаточно надежной, чтобы изящно умереть, если она не может написать соответствующий файл.

0 голосов
/ 01 мая 2019

Просто сделай это (и убери за собой)

Возможным подходом может быть попытка создать файл и в конечном итоге удалить его, если создание прошло успешно, но я надеюсь, что есть более элегантный способ достижения того же результата.

Возможно, это самый надежный способ.

Ниже указано 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 и созданием фактического файла содержимое и разрешения вашей файловой системы могли измениться.

0 голосов
/ 15 октября 2018

Вот кое-что, что вы можете сделать, которое работает в разных операционных системах Использование соответствия регулярному выражению для проверки существующих известных недопустимых символов.

if (newName.matches(".*[/\n\r\t\0\f`?*\\<>|\":].*")) {
    System.out.println("Invalid!");
} else {
    System.out.println("Valid!");
}

Плюсы

  • Это работает в операционных системах
  • Вы можете настроить его любым способом Вы хотите, отредактировав это регулярное выражение.

Против

  • Возможно, это не полный список, и для его заполнения требуется больше недопустимых шаблонов или символов.
0 голосов
/ 14 июля 2011
boolean canWrite(File file) {
  if (file.exists()) {
    return file.canWrite();
  }
  else {
    try {
      file.createNewFile();
      file.delete();
      return true;
    }
    catch (Exception e) {
      return false;
    }
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...