Есть ли надежный способ проверить существование файла в Linux NFS?

Я работаю над программой Java, которая требует проверки существования файлов.

Ну, достаточно просто, код использует вызовы File.exists() для проверки существования файла. И проблема у меня в том, что он сообщает о ложном срабатывании. Это означает, что файл на самом деле не существует, но метод exists() возвращает true. Никаких исключений не было зафиксировано (по крайней мере, таких исключений, как устаревший дескриптор NFS). Программа даже смогла прочитать файл через InputStream, получив 0 байт, как и ожидалось, и все же не исключение. Целевым каталогом является Linux NFS. И я на 100% уверен, что искомый файл никогда не существует.

Я знаю, что для java.io.File.exists() существуют известные ошибки (вид ограничения API). Поэтому я добавил еще один способ, проверяя существование файла с помощью команды Linux ls. Вместо вызова File.exists() код Java теперь запускает команду Linux для ls целевого файла. Если код выхода 0, файл существует. В противном случае файл не существует.

Количество обращений к проблеме, кажется, уменьшилось с введением этого трюка, но все еще всплывает. Опять же, нигде не было зафиксировано никаких ошибок (на этот раз стандартный вывод). Это означает, что проблема настолько серьезна, что даже родная команда Linux не исправит ее в 100% случаев.

Итак, есть пара вопросов:

  1. Я считаю, что хорошо известная проблема Java с File.exists() связана с сообщением о ложном отрицательном результате. Где сообщалось, что файл не существует, но на самом деле он существует. Поскольку API не выдает IOException вместо File.exists(), он решает проглотить исключение в случае сбоя вызовов базовых собственных функций ОС, например. Тайм-аут NFS. Но тогда это не объясняет случай ложного срабатывания, который у меня есть, учитывая, что файл никогда не существует. Любой бросок на этом?
  2. Насколько я понимаю, код выхода ls в Linux таков: 0 означает «все в порядке», эквивалент файла существует. Является ли это понимание неправильным? На справочной странице ls не так ясно объясняется значение кода выхода: статус выхода: 0, если все в порядке, 1, если небольшие проблемы, 2, если серьезные проблемы.
  3. Ладно, вернемся к теме. Есть ли верный способ проверить существование файла с помощью Java в Linux? Прежде чем мы увидим официально выпущенный JDK7 с NIO2.

person Stanley    schedule 22.09.2011    source источник
comment
Вы создаете и удаляете файл в том же процессе, в котором проверяете его существование? Вы никогда не должны получать сообщение о том, что файл существует, если он никогда не существовал, но если он был недавно удален, вы можете получить другое поведение (например, файлы .nfs923126).   -  person evil otto    schedule 22.09.2011
comment
File.isFile ведет себя лучше?   -  person MeBigFatGuy    schedule 22.09.2011
comment
Спасибо за ответ. Кажется, я только что понял, почему я получил ложноположительный результат. Это действительно не File.exists() не работает, но состояние гонки в домашнем фоновом считывателе файлов заставляет чтение файла продолжаться в отсутствие файла. Я только что исправил ридер, и теперь все снова в порядке, насколько я могу тестировать.   -  person Stanley    schedule 22.09.2011
comment
Отто, вы правы в том, что если файл недавно существует и был удален, он может демонстрировать ложноположительное поведение. Но я думаю, что это условие не существует в моей среде, что теперь подтверждается результатами последнего раунда тестирования. Еще раз спасибо всем за участие. Я хотел бы, чтобы JDK7 был выпущен к настоящему времени, чтобы он мог сэкономить мое время, убрав проблемный java.io из списка подозреваемых: P Почему бы им просто не сделать резервную копию NIO2, если выпуск 7 может занять вечность? Хм? Шучу, типа :)   -  person Stanley    schedule 22.09.2011
comment
Большинство файловых операций передаются непосредственно в ОС (за исключением буферизованных данных), поэтому, если Java не дает вам правильного результата, скорее всего, это то, что вы делаете, или ошибка в JVM или ОС. Маловероятно, что изменение методов, которые вы используете в Java, поможет.   -  person Peter Lawrey    schedule 22.09.2011
comment
Дубликат альтернативы File.exists() в Java   -  person BuZZ-dEE    schedule 08.12.2020


Ответы (3)


JDK7 был выпущен несколько месяцев назад. В классе Files есть методы exists и notExists, но они возвращают boolean, а не выдают исключение. Если вам действительно нужно исключение, используйте FileSystems.getDefault().provider().checkAccess(path), и оно выдаст исключение, если файл не существует.

person Alan    schedule 22.09.2011
comment
Да, это то, что я также прочитал в сообщении об ошибке Sun. Используйте File.toPath().checkAccess(). Приятно слышать, что JDK7 выпущен. Спасибо. - person Stanley; 23.09.2011
comment
Это не работает должным образом в сценарии с сетевым файлом.Path path=FileSystems.getDefault().provider().getPath(f.toURI()); FileSystems.getDefault().provider().checkAccess(path,AccessMode.READ); в тестовом примере JUnit в моем ответе произойдет сбой. - person Wolfgang Fahl; 21.04.2014

Вот тест JUnit, который показывает проблему и некоторый код Java, который фактически пытается прочитать файл.

Проблема возникает, например. используя Samba на OSX Mavericks. Возможная причина объясняется заявлением в: http://appleinsider.com/articles/13/06/11/apple-shifts-from-afp-file-sharing-to-smb2-in-os-x-109-mavericks

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

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

Тест JUnit:

/**
 * test file exists function on Network drive replace the testfile name and ssh computer
     * with your actual environment
 * @throws Exception
 */
@Test
public void testFileExistsOnNetworkDrive() throws Exception {
    String testFileName="/Volumes/bitplan/tmp/testFileExists.txt";
    File testFile=new File(testFileName);
    testFile.delete();
    for (int i=0;i<10;i++) {
        Thread.sleep(50);
        System.out.println(""+i+":"+OCRJob.checkExists(testFile));
        switch (i) {
        case 3:
            // FileUtils.writeStringToFile(testFile, "here we go");
            Runtime.getRuntime().exec("/usr/bin/ssh phobos /usr/bin/touch "+testFileName);
            break;
        }
    }
}

Исходный код checkExists:

/**
 * check if the given file exists
 * @param f
 * @return true if file exists
 */
public static boolean checkExists(File f)  {
    try {
        byte[] buffer = new byte[4];
        InputStream is = new FileInputStream(f);
        if (is.read(buffer) != buffer.length) { 
            // do something 
        }
        is.close();
        return true;
    } catch (java.io.IOException fnfe) {

    }
    return false;
}
person Wolfgang Fahl    schedule 21.04.2014

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

Единственный безопасный способ узнать, существует ли файл и можно ли его прочитать, — это фактически прочитать данные из файла. Независимо от файловой системы - локальной или удаленной. Причиной является состояние гонки, которое может возникнуть сразу после того, как вы получите успех от checkAccess(path): проверьте, затем откройте файл, и вы вдруг обнаружите, что он не существует. Какой-то другой поток (или другой удаленный клиент) мог удалить его или получить монопольную блокировку. Так что не утруждайте себя проверкой доступа, а попробуйте прочитать файл. Время, проведенное в беге ls, просто облегчает вписывание окна состояния гонки.

person Matej Kovac    schedule 12.10.2012