如何使用Java测试文件是否“完整”(完全编写)

假设您有一个外部进程将文件写入某个目录,并且您有一个单独的进程定期尝试从该目录中读取文件。 要避免的问题是读取另一个进程当前正在写出的文件,因此它将是不完整的。 当前,读取的进程使用最小文件年龄计时器检查,因此它将忽略所有文件,除非它们的上次修改日期超过XX秒。

我想知道是否有更清洁的方法来解决这个问题。 如果文件类型未知(可能是多种不同的格式),是否有一些可靠的方法来检查文件头中应该在文件中的字节数,以及文件中当前确认它们匹配的字节数?

感谢您的任何想法或想法!

您可以使用外部标记文件。 写入过程可以在开始创建文件XYZ之前创建文件XYZ.lock,并在XYZ完成后删除XYZ.lock。 然后,读者可以很容易地知道,只有当相应的.lock文件不存在时,它才能认为文件是完整的。

我过去这样做的方式是编写文件的进程写入“temp”文件,然后在文件写完后将文件移动到读取位置。

因此编写过程将写入info.txt.tmp 。 完成后,它会将文件重命名为info.txt 。 然后阅读过程只需检查info.txt的存在 – 它知道如果它存在,它已被完全写入。

或者,您可以让写入过程将info.txt写入另一个目录,如果您不喜欢使用奇怪的文件扩展名,则将其移动到读取目录。

我无法使用临时标记等,因为客户端通过密钥对SFTP上传文件。 它们的尺寸可能非常大。

它相当hacky但我比较睡眠前后几秒钟的文件大小。

它显然不是理想的锁定线程,但在我们的情况下它只是作为后台系统进程运行所以似乎工作正常

 private boolean isCompletelyWritten(File file) throws InterruptedException{ Long fileSizeBefore = file.length(); Thread.sleep(3000); Long fileSizeAfter = file.length(); System.out.println("comparing file size " + fileSizeBefore + " with " + fileSizeAfter); if (fileSizeBefore.equals(fileSizeAfter)) { return true; } return false; } 

注意:如下所述,这可能不适用于Windows。 这是在Linux环境中使用的。

我在过去使用Windows的这种情况下使用的一个简单解决方案是使用boolean File.renameTo(File)并尝试将原始文件移动到单独的暂存文件夹:

 boolean success = potentiallyIncompleteFile.renameTo(stagingAreaFile); 

如果successfalse ,则仍然会写入potentiallyIncompleteFile

即使字节数相等,文件的内容也可能不同。

所以我认为,你必须逐字节地匹配旧文件和新文件。

似乎可以解决此问题的2个选项:

  1. 最好的选项 – 作者进程以某种方式通知阅读过程写作完成。
  2. 将文件写入{id} .tmp,而不是将其重命名为{id} .java,并且读取过程仅在* .java文件上运行。 重命名花费的时间少得多,并且这两个过程一起工作的机会减少了。

首先, 为什么在复制到Samba共享时OS X没有像Windows一样锁定文件? 但这是你已经在做的变化。

就读取任意文件和查找大小而言,某些文件具有该信息,有些文件没有,但即使是那些没有任何常用方式来表示它们。 您需要每种格式的特定信息,并分别独立管理。

如果您绝对必须对文件执行“即时”操作,那么您的编写过程将需要发送某种通知。 否则,您几乎不会轮询文件,与从随机文件中读取随机块相比,读取目录在I / O方面相当便宜。

这可以通过使用Apache Commons IO maven库FileUtils.copyFile()方法来实现。 如果您尝试复制文件并获取IOException,则表示文件未完全保存。

例:

 public static void copyAndDeleteFile(File file, String destinationFile) { try { FileUtils.copyFile(file, new File(fileDirectory)); } catch (IOException e) { e.printStackTrace(); copyAndDeleteFile(file, fileDirectory, delayThreadPeriod); } 

或者定期检查包含此文件的文件夹的延迟大小:

 FileUtils.sizeOfDirectory(folder);