Java:监视目录以移动大文件

2022-09-01 09:40:13

我一直在编写一个监视目录的程序,当在其中创建文件时,它会更改名称并将其移动到新目录。在我的第一个实现中,我使用了Java的Watch Service API,当我测试1kb文件时,它工作正常。出现的问题是,实际上创建的文件在50-300mb之间。发生这种情况时,观察程序 API 会立即找到该文件,但无法移动它,因为它仍在写入中。我尝试将观察程序放在一个循环中(这会生成异常,直到文件可以移动),但这似乎非常低效。

由于这不起作用,我尝试使用计时器,该计时器每10秒检查一次文件夹,然后在可能的情况下移动文件。这就是我最终选择的方法。

问:当文件完成写入时,是否要发出信号,而无需执行异常检查或不断比较大小?我喜欢为每个文件使用一次观察程序 API 的想法,而不是不断使用计时器进行检查(并遇到异常)。

非常感谢所有回复!

nt


答案 1

我今天遇到了同样的问题。我的用例在文件实际导入之前有一小段延迟并不是一个大问题,我仍然想使用NIO2 API。我选择的解决方案是等到文件没有被修改10秒钟后再对其执行任何操作。

实现的重要部分如下。程序将一直等到等待时间到期或发生新事件。每次修改文件时,都会重置过期时间。如果在等待时间到期之前删除了某个文件,则会将其从列表中删除。我使用具有预期过期时间超时的轮询方法,即(上次修改+等待时间)-当前时间

private final Map<Path, Long> expirationTimes = newHashMap();
private Long newFileWait = 10000L;

public void run() {
    for(;;) {
        //Retrieves and removes next watch key, waiting if none are present.
        WatchKey k = watchService.take();

        for(;;) {
            long currentTime = new DateTime().getMillis();

            if(k!=null)
                handleWatchEvents(k);

            handleExpiredWaitTimes(currentTime);

            // If there are no files left stop polling and block on .take()
            if(expirationTimes.isEmpty())
                break;

            long minExpiration = min(expirationTimes.values());
            long timeout = minExpiration-currentTime;
            logger.debug("timeout: "+timeout);
            k = watchService.poll(timeout, TimeUnit.MILLISECONDS);
        }
    }
}

private void handleExpiredWaitTimes(Long currentTime) {
    // Start import for files for which the expirationtime has passed
    for(Entry<Path, Long> entry : expirationTimes.entrySet()) {
        if(entry.getValue()<=currentTime) {
            logger.debug("expired "+entry);
            // do something with the file
            expirationTimes.remove(entry.getKey());
        }
    }
}

private void handleWatchEvents(WatchKey k) {
    List<WatchEvent<?>> events = k.pollEvents();
    for (WatchEvent<?> event : events) {
        handleWatchEvent(event, keys.get(k));
    }
    // reset watch key to allow the key to be reported again by the watch service
    k.reset();
}

private void handleWatchEvent(WatchEvent<?> event, Path dir) throws IOException {
    Kind<?> kind = event.kind();

    WatchEvent<Path> ev = cast(event);
        Path name = ev.context();
        Path child = dir.resolve(name);

    if (kind == ENTRY_MODIFY || kind == ENTRY_CREATE) {
        // Update modified time
        FileTime lastModified = Attributes.readBasicFileAttributes(child, NOFOLLOW_LINKS).lastModifiedTime();
        expirationTimes.put(name, lastModified.toMillis()+newFileWait);
    }

    if (kind == ENTRY_DELETE) {
        expirationTimes.remove(child);
    }
}

答案 2

写入另一个文件作为原始文件已完成的指示。I.g 'fileorg.dat' 如果完成,创建一个文件 'fileorg.done' 并仅检查 'fileorg.done' 正在增长。

使用巧妙的命名约定,您应该不会有问题。


推荐