Java FileChannel.size() 与 File.length() - 在 FileChannel.truncate() 后

5

我在考虑将这个问题转换成我的情况。然后我决定我的情况需要自己的问题和希望有答案。在调用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()匹配?刷新文件的元数据是可以的,只要不强制刷新文件的内容即可。刷新文件的内容会产生不必要的文件I/O。
不接受建议我使用channelSize或fileLengthOpen或忽略该问题的答案。我有另一个类文件使用File.length()来确定文件的长度。将channelSize或fileLengthOpen传递给另一个类需要将该值向上传递几个帧,然后再向下传递多个帧。如果您建议我使用Files.size()来解决该问题,请说明原因。
我不确定这是否重要。我在Linux上运行Java 10。(是的,我知道Java 10很老,但在我实施升级到Java 11所需的功能之前,我被卡住了。)
编辑:过去,在文件上的多线程更新会造成问题。因此,我创建了文件锁定机制,以便整个过程中只有1个线程可以独立操作文件(或多个线程可以读取文件)。此外,传递给我的truncate()的File对象由调用线程创建,仅由该线程使用。我可以保证进程中没有其他线程可以操作磁盘上的文件或File对象。
编辑:我更改了代码以在channel.close()之前和之后调用Files.size()。channel.size()、File.length()和Files.size()在channel打开时报告正确的大小。在channel.close()之后立即,File.length()和Files.size()偶尔会出现原始更长文件长度而不是截断的较短长度。

1
为什么你不使用 Files.size() 呢?File 类应该被允许安息了。你试过用 Files.size() 来解决这个问题吗? - RealSkeptic
众所周知,Windows 在文件打开时不会更新文件元数据。 - user207421
我在我的代码库中大约有90个地方使用了 File.length()。如果我能证明它可以解决问题,我将对代码进行更改以使用Files.size()。我已经修改了truncate(),在关闭FileChannel之前和之后检查Files.size(),然后观察结果......可能需要几天时间才能复现这个问题。 - Nathan
不幸的是,在Windows上无法重现这个问题。但是,这并不能说明什么,因为在Linux上这个问题并不经常出现。 - Nathan
@RealSkeptic 我在使用 Files.size() 后更新了问题。Files.size()File.length() 存在相同的问题。 - Nathan
1个回答

1

等待fileLengthClosed正确,这是代码。

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

   if (fileLengthClosed == size)
      break;

   Thread.sleep(1);
}

我已经运行了这段代码三周,没有出现任何问题。一个担忧是如果文件长度被修改或者file.length()从未更新,那么就没有超时代码。
这个解决方案并没有回答为什么File.length()一开始就是不正确的。另外,调用Thread.sleep()表明我正在等待某个操作完成,我真的应该强制该操作完成或在该操作上进行阻塞。我不确定如何强制或阻塞该操作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接