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

时间:2012-04-05 13:18:02

标签: java

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

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

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

10 个答案:

答案 0 :(得分:11)

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

答案 1 :(得分:10)

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

因此写作过程将写入 info.txt.tmp 。完成后,它会将文件重命名为 info.txt 。然后,阅读过程只需要检查是否存在 info.txt - 它知道如果存在,则完全写入。

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

答案 2 :(得分:5)

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

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

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

var employees = [
    {"firstName":"John", "lastName":"Doe"},
    {"firstName":"Anna", "lastName":"Smith"},
    {"firstName":"Peter","lastName": "Jones"}
];

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

答案 3 :(得分:4)

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

boolean success = potentiallyIncompleteFile.renameTo(stagingAreaFile);

如果successfalse,则potentiallyIncompleteFile仍在写入。

答案 4 :(得分:2)

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

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

答案 5 :(得分:2)

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

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

答案 6 :(得分:2)

首先,有Why doesn't OS X lock files like windows does when copying to a Samba share?,但这是你正在做的事情的变化。

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

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

答案 7 :(得分:2)

这可以通过使用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);

答案 8 :(得分:1)

另一种测试文件是否完全写入的方法:

private void waitUntilIsReadable(File file) throws InterruptedException {
    boolean isReadable = false;
    int loopsNumber = 1;
    while (!isReadable && loopsNumber <= MAX_NUM_OF_WAITING_60) {
        try (InputStream in = new BufferedInputStream(new FileInputStream(file))) {
            log.trace("InputStream readable. Available: {}. File: '{}'",
                    in.available(), file.getAbsolutePath());
            isReadable = true;
        } catch (Exception e) {
            log.trace("InputStream is not readable yet. File: '{}'", file.getAbsolutePath());
            loopsNumber++;
            TimeUnit.MILLISECONDS.sleep(1000);
        }
    }
}

答案 9 :(得分:0)

如果要使用FTP或Winscp传输文件,请在Unix上使用此功能

public static void isFileReady(File entry) throws Exception {
        long realFileSize = entry.length();
        long currentFileSize = 0;
        do {
            try (FileInputStream fis = new FileInputStream(entry);) {
                currentFileSize = 0;
                while (fis.available() > 0) {
                    byte[] b = new byte[1024];
                    int nResult = fis.read(b);
                    currentFileSize += nResult;
                    if (nResult == -1)
                        break;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("currentFileSize=" + currentFileSize + ", realFileSize=" + realFileSize);
        } while (currentFileSize != realFileSize);
    }