25

目前,我正在开发一个项目,该项目在其中一个例程中处理源目录中的文件。有一个 Java 进程正在寻找指定的目录并尝试读取和处理文件(如果存在)。文件退出大并由其他第三方进程更新。问题是如何检查文件是否已完全写入?我正在尝试使用file.length(),但看起来即使写入过程尚未完成,它也会返回实际大小。我觉得解决方案应该依赖于平台。任何帮助,将不胜感激。

更新:这个问题与重复的问题并没有什么不同,但它的答案是高度评价的工作代码片段。

4

6 回答 6

22

我得到了解决方案:

private boolean isCompletelyWritten(File file) {
    RandomAccessFile stream = null;
    try {
        stream = new RandomAccessFile(file, "rw");
        return true;
    } catch (Exception e) {
        log.info("Skipping file " + file.getName() + " for this iteration due it's not completely written");
    } finally {
        if (stream != null) {
            try {
                stream.close();
            } catch (IOException e) {
                log.error("Exception during closing file " + file.getName());
            }
        }
    }
    return false;
}

感谢@cklab 和@Will 以及所有其他建议以“独占锁定”方式查看的人。我只是在这里发布代码,让其他有兴趣的人使用它。我相信@tigran 建议的重命名解决方案也有效,但纯 Java 解决方案对我来说更可取。

PS 最初我使用FileOutputStream而不是,RandomAccessFile但它会锁定正在写入的文件。

于 2012-06-28T10:27:46.840 回答
11

生产者进程在完成写入后是否关闭文件?如果是这样,如果生产者进程仍在生产,则尝试使用排他锁在消费者进程中打开文件将失败。

于 2012-06-28T07:02:35.400 回答
5

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

boolean success = potentiallyIncompleteFile.renameTo(stagingAreaFile);

如果successis false,则potentiallyIncompleteFile仍在写入。

于 2013-07-11T21:12:49.927 回答
3

我不认为有一个通用的解决方案。查找文件大小是错误的,因为某些应用程序可以在任何写入调用之前设置文件大小。一种可能性是使用锁定。这将要求作者产生一个写锁(或排他锁)。如果你不能修改 writer,那么你可以使用 OS 提供的工具,比如 Linux 上的 fuser 来查看是否有一个仍在访问文件的进程。

于 2012-06-28T07:06:27.407 回答
2

如果您计划在单个平台上使用此代码,您可以使用NIO 的 FileLock 设施。但请仔细阅读文档,并注意在许多平台上,锁只是建议性的。

另一种方法是让一个进程使用您的进程无法识别的名称写入文件,然后在写入完成后将文件重命名为可识别的名称。在大多数平台上,如果源和目标是相同的文件系统卷,则重命名操作是原子操作。

于 2012-06-28T07:10:48.857 回答
1

如果您可以使用 Java 1.7,请查看 NIO 工具,特别是java.nio.channels.FileChannel

是锁定文件并读取它的示例。

于 2013-06-05T16:24:38.230 回答