Подтвердить что ты не робот

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

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

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

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

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

4b9b3361

Ответ 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;
    }
  }
}