Я думал о том, чтобы изменить этот вопрос в моей ситуации. Затем я решил, что моя ситуация требует отдельного вопроса и, надеюсь, ответов. После вызова 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()
в редких случаях указывается исходная более длинная длина файла, а не усеченная более короткая длина.
Известно, что Windows не обновляет метаданные файла, пока файл открыт.
В моей кодовой базе около 90 мест, где используется File.length()
. Я изменю код на Files.size()
, если смогу доказать, что это решает проблему. Я изменил truncate()
, чтобы проверить Files.size()
до и после закрытия FileChannel
, и я посмотрю, что произойдет... это может занять несколько дней, чтобы воспроизвести проблему.
К сожалению, проблема не воспроизводится в Windows. Но это мало о чем говорит, так как проблема не очень часто воспроизводится в Linux.
@RealSkeptic Я обновил вопрос после использования Files.size()
. Files.size()
имеет ту же проблему, что и File.length()
.
Подождите, пока fileLengthClosed
будет правильным. Вот код.
while (true)
{
fileLengthClosed = file.length();
if (fileLengthClosed == size)
break;
Thread.sleep(1);
}
Я запускал этот код в течение 3 недель, и проблем не было. Одна из проблем заключается в том, что нет кода тайм-аута на случай, если длина файла изменена или file.length()
никогда не обновляется.
Это решение не отвечает, почему File.length()
вообще неверно. Кроме того, вызов Thread.sleep()
предполагает, что я действительно жду завершения какой-то другой операции, и я действительно должен принудительно завершить эту операцию или заблокировать эту операцию. Я не уверен, как заставить или заблокировать эту операцию.
Почему не вы все равно используете
Files.size()
? КлассуFile
уже должно быть позволено умереть спокойно. Вы пытались узнать, есть ли у вас такая проблема сFiles.size()
?