Java FileChannel.size() против File.length() — после FileChannel.truncate()

Я думал о том, чтобы изменить этот вопрос в моей ситуации. Затем я решил, что моя ситуация требует отдельного вопроса и, надеюсь, ответов. После вызова FileChannel.truncate() для уменьшения размера файла я вызываю FileChannel.size(), закрываю FileChannel и затем вызываю File.length(). Файл существует на протяжении всей операции. FileChannel.size() всегда точен. В редких случаях File.length() возвращает размер файла до truncate().

Вот код, который показывает ситуацию.

public static void truncate(File file, long size) throws IOException
{
   FileChannel channel;
   Path path;
   long channelSize, fileLengthOpen, fileLengthClosed;

   path    = file.toPath();
   channel = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);

   try
   {
      channel.truncate(size);

      channelSize    = channel.size();
      fileLengthOpen = file.length();
   }
   finally
   {
      channel.close();
   }

   fileLengthClosed = file.length();

   if ((channelSize != size) || (fileLengthOpen != size) || (fileLengthClosed != size))
      throw new IOException("The channel size or file length does not match the truncate size.  Channel: " + channelSize + " - Open File: " + fileLengthOpen + " - Closed File: " + fileLengthClosed + " - Truncate: " + size);
}

В редких случаях код выдает IOException. channelSize == size и fileLengthOpen == size, но fileLengthClosed != size.

Почему fileLengthClosed != size? Как мне убедиться, что File.length() соответствует FileChannel.size()? Сброс метаданных файла будет нормальным, если он не приводит к принудительному сбросу содержимого файла. Сброс содержимого файла повлечет за собой ненужные файловые операции ввода-вывода.

Ответы, говорящие мне использовать channelSize или fileLengthOpen или игнорировать проблему, не принимаются. У меня есть другой файл класса, который использует File.length() для определения длины файла. Передача channelSize или fileLengthOpen другому классу потребует передачи значения вверх по стеку на несколько кадров, а затем обратно на несколько кадров вниз. Если вы предлагаете мне использовать Files.size() для решения проблемы, объясните, почему.

Я не уверен, имеет ли это значение. Я использую Java 10 в Linux. (Да, я знаю, что Java 10 устарела, но я застрял с ней, пока не смогу реализовать необходимые функции для обновления до Java 11.)

Обновлено: в прошлом многопоточные обновления файлов вызывали проблемы. По этой причине я создал механизм блокировки файлов, чтобы только 1 поток во всем процессе мог исключительно работать с файлом (или несколько потоков могли читать файл). Кроме того, объект File, переданный моему truncate(), был создан вызывающим потоком и используется только этим потоком. Я могу гарантировать, что никакой другой поток в процессе не сможет работать с файлом на диске или объектом File.

Обновлено: я изменил код для вызова Files.size() до и после channel.close(). channel.size(), File.length() и Files.size() сообщают правильный размер, пока channel открыт. Сразу после channel.close(), File.length() и Files.size() в редких случаях указывается исходная более длинная длина файла, а не усеченная более короткая длина.

Почему не вы все равно используете Files.size()? Классу File уже должно быть позволено умереть спокойно. Вы пытались узнать, есть ли у вас такая проблема с Files.size()?

RealSkeptic 07.04.2019 17:29

Известно, что Windows не обновляет метаданные файла, пока файл открыт.

user207421 07.04.2019 17:52

В моей кодовой базе около 90 мест, где используется File.length(). Я изменю код на Files.size(), если смогу доказать, что это решает проблему. Я изменил truncate(), чтобы проверить Files.size() до и после закрытия FileChannel, и я посмотрю, что произойдет... это может занять несколько дней, чтобы воспроизвести проблему.

Nathan 07.04.2019 18:04

К сожалению, проблема не воспроизводится в Windows. Но это мало о чем говорит, так как проблема не очень часто воспроизводится в Linux.

Nathan 07.04.2019 18:05

@RealSkeptic Я обновил вопрос после использования Files.size(). Files.size() имеет ту же проблему, что и File.length().

Nathan 12.04.2019 20:07
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
5
5
305
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Подождите, пока fileLengthClosed будет правильным. Вот код.

while (true)
{
   fileLengthClosed = file.length();

   if (fileLengthClosed == size)
      break;

   Thread.sleep(1);
}

Я запускал этот код в течение 3 недель, и проблем не было. Одна из проблем заключается в том, что нет кода тайм-аута на случай, если длина файла изменена или file.length() никогда не обновляется.

Это решение не отвечает, почему File.length() вообще неверно. Кроме того, вызов Thread.sleep() предполагает, что я действительно жду завершения какой-то другой операции, и я действительно должен принудительно завершить эту операцию или заблокировать эту операцию. Я не уверен, как заставить или заблокировать эту операцию.

Другие вопросы по теме