FileNotFoundexception 即使文件在 java 的监视服务期间就位

FileNotFound exception even though file is in the place during watch service in java

我在文件夹上有监视服务 运行,当我尝试使用 evenKind == 修改修改现有文件时(基本上是粘贴相同的文件而不删除当前文件),我收到 FileNotFoundException(该进程无法访问该文件,因为它正被另一个进程使用。)

if (eventKind == StandardWatchEventKinds.ENTRY_MODIFY) {
                
                String newFileChecksum = null;
                
                if (eventPath.toFile().exists()) {
                    newFileChecksum = getFileChecksum(eventPath.toFile());

                }
                        
                if (fileMapper.containsKey(eventPath)) {
                    String existingFileChecksum = fileMapper.get(eventPath);


                if (!existingFileChecksum.equals(newFileChecksum)) {

                        fileMapper.replace(eventPath, existingFileChecksum, newFileChecksum);
                        
                        log.info("listener.filemodified IN");
                        for (DirectoryListener listener : this.listeners) {
                            
                            listener.fileModified(this, eventPath);
                        }
                        log.info("listener.filemodified OUT");
                    } else {
                        log.info("existing checksum");
                        log.debug(String.format(
                                "Checksum for file [%s] has not changed. Skipping plugin processing.",
                                eventPath.getFileName()));
                    }
         
                }
    
            }

在代码中当...getFileChecksum()被调用时

if (eventPath.toFile().exists()) {

        newFileChecksum = getFileChecksum(eventPath.toFile());

}

所以理想情况下,eventPath.toFile().exists() 是 TRUE,因此如果 getFileChecksum() 被调用,它转到方法...

private synchronized String getFileChecksum(File file) throws IOException, NoSuchAlgorithmException {
        
        MessageDigest md5Digest = MessageDigest.getInstance("MD5");
        
        FileInputStream fis = null;
        
        if(file.exists()) {
            
            try {
                fis = new FileInputStream(file);
            } catch(Exception e) {
                e.printStackTrace();
            }
        } else {
            log.warn("File not detected.");
        }
         
        byte[] byteArray = new byte[1024];
        
        int bytesCount = 0; 
     
        while ((bytesCount = fis.read(byteArray)) != -1) {
            
            md5Digest.update(byteArray, 0, bytesCount);
        };
 
        fis.close();
        
        byte[] bytes = md5Digest.digest();
       
        StringBuilder stringBuilder = new StringBuilder();
        
        for (int i=0; i< bytes.length ;i++) {
            
            stringBuilder.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
        }
         
       return stringBuilder.toString();
    }
}

出现异常 fis = new FileInputStream(file); 即使文件存在于文件夹中。

FileNotFoundException(进程无法访问该文件,因为它正被另一个进程使用。)

我创建了一个 RandomAccessFile 和一个通道来释放文件上的任何锁定,但它不起作用。请建议这里可能发生的事情。

//更新 --> 这是我的无限 while 循环,

发生了什么事?当我放置一个文件时 1 create2 update 被调用,假设,当我删除文件时,1 delete 1 modify 正在被调用,如果我将相同的文件放回文件夹,我会创建,但在 CREATE 完成之前,正在调用 MODIFY。创建不是 运行 而修改是 运行.

我通过将 Thread.sleep(500) 放在

之间解决了这个问题
WatchKey wk = watchService.take();
Thread.sleep(500)
        for (WatchEvent<?> event : wk.pollEvents()) {

但我认为我不能证明在这里使用 sleep 是合理的。请帮助

WatchService watchService = null; WatchKey watchKey = null;

        while (!this.canceled && (watchKey == null)) {

           watchService = watchService == null
                        ? FileSystems.getDefault().newWatchService() : watchService;
                watchKey = this.directory.register(watchService,
                        StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE,
                        StandardWatchEventKinds.ENTRY_CREATE);
         }
         while (!this.canceled) {

    try {

        WatchKey wk = watchService.take();
        
        for (WatchEvent<?> event : wk.pollEvents()) {

            Kind<?> eventKind = event.kind();
            
            System.out.println("Event kind : " + eventKind);

            Path dir = (Path)wk.watchable();
            Path eventPath = (Path) event.context();

            Path fullPath = dir.resolve(eventPath);
            fireEvent(eventKind, fullPath);
        }

        wk.reset();

    }

让我猜猜...

修改文件时调用修改事件。要修改文件,您最有可能使用单独的工具(如记事本)打开并锁定文件。

你的观察者收到一个文件被修改的事件(现在)但是你不能再次修改它(fileinputstream 想要做的)因为它已经被锁定了。

WatchService 很冗长,可能会报告多个 ENTRY_MODIFY 事件以进行保存操作 - 即使另一个应用程序正在执行或重复写入。您的代码可能正在处理修改事件,而另一个应用程序仍在编写,并且可能还有第二个 ENTRY_MODIFY 正在处理。

使用 WatchService 的一个更安全的策略是整理您收到的事件,并且仅在出现暂停时才对更改采取行动。这样的事情将确保您在第一个事件上阻塞,然后以较小的超时轮询监视服务以查看在您对前一组进行操作之前是否存在更多更改:

WatchService ws = ...
HashSet<Path> modified = new HashSet<>();

while(appIsRunning) {
    int countNow = modified.size();
    WatchKey k = countNow == 0 ? ws.take() : ws.poll(1, TimeUnit.MILLISECONDS);
    if (k != null) {
        // Loop through k.pollEvents() and put modify file path into modified set:
        // DO NOT CALL fireEvent HERE, save the path instead:
        ...
        if (eventKind == ENTRY_MODIFY)
            modified.add(filePath);
    }
    // Don't act on changes unless no new events:
    if (countNow == modified.size()) {
        // ACT ON modified list here - the watch service did not report new changes
        for (Path filePath : modified) {
           // call fireEvent HERE:
           fireEvent(filePath);
        }

        // reset the list so next watch call is take() not poll(1)
        modified.clear();
    }
}

如果您也在寻找带有 MODIFY 的 CREATE 和 DELETE 操作,您将不得不整理并忽略一些较早的事件,因为最后记录的事件类型可能优先于先前记录的类型。例如,如果调用 take() 然后 poll(1) 直到没有新的报告:

  1. 先删除再创建 => 你可能想考虑修改
  2. 任何 CREATE 然后 MODIFY => 你可能想考虑 CREATE
  3. 任何 CREATE 或 MODIFY 然后 DELETE => 视为 DELETE

您的逻辑还希望仅在 modified.size() + created.size() + deleted.size() 的值在两次运行之间发生变化时才起作用。

我有一个更好的方法,像这样在 var isFileReady 上使用 while 循环...

var isFileReady = false;

while(!isFile...) {
}

在里面创建一个 try and catch。

try {

FileInputStream fis = new FileInputStream();

isFileReady = true;
} catch () {
catch exception or print file not ready.
}

这将解决您的问题。