根据此文档:http : //technet.microsoft.com/en-us/library/ff686200%28v=ws.10%29.aspx File.exists() 在 smb2 网络共享上不准确。我无法更改任何寄存器设置,所以我想处理它。根据文档,有一个 API 可以从文件系统获取通知。我假设 WatchService 是这个 API 的 Java 实现。我对么?
我从 jdk 示例中的 WatchDir 示例开始,并对其进行了一些剥离。我只需要知道何时创建和删除文件(我不关心文件修改)。为了进行测试,我在触发新事件时添加了新的 File.exists()。我还启动了一个单独的线程来测试文件是否存在。当我不启动这个单独的线程时,文件存在立即返回 true。当额外的线程启动时,它不再准确。我需要一个更准确的 file.exists 检查整个应用程序和所有正在运行的线程。
为了测试,我使用了 2 台 Windows 7 电脑(运行 java 7)(默认启用 smb2)。工作目录必须在远程 pc 上,并且文件 test.txt 必须在远程 pc 上创建(或从另一个文件夹复制)(不使用网络驱动器,但在 pc 本身上)。
这是我的测试代码:
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;
import java.io.File;
import java.io.IOException;
import java.nio.file.ClosedWatchServiceException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
public class WatchDir
{
private final WatchService _watcher;
private final String _dir;
public WatchDir( String dir ) throws IOException
{
_dir = dir;
_watcher = FileSystems.getDefault().newWatchService();
Paths.get( dir ).register( _watcher, ENTRY_CREATE, ENTRY_DELETE );
System.out.println( "watch registered for dir: " + dir );
}
public void run()
{
try
{
while ( true )
{
WatchKey key = _watcher.take();
for ( WatchEvent<?> event : key.pollEvents() )
{
WatchEvent.Kind<?> kind = event.kind();
if ( kind == OVERFLOW )
continue;
@SuppressWarnings( "unchecked" )
WatchEvent<Path> ev = (WatchEvent<Path>)event;
Path fileName = ev.context();
System.out.println( "WatchDir event: " + kind.name() + ": " + fileName );
if ( kind == ENTRY_CREATE )
{
String realPath = _dir + fileName;
System.out.println( "WatchDir: " + realPath + " exists == " + new File( realPath ).exists() );
}
}
key.reset();
}
}
catch ( ClosedWatchServiceException x )
{
return;
}
catch ( InterruptedException ex )
{
return;
}
}
public static void main( String[] args )
{
Thread t = new Thread( new Runnable()
{
@Override
public void run()
{
try
{
while ( true )
{
String filename = "subdir\\test.txt";
boolean fileExists = new File( filename ).exists();
System.err.println( "FileExistsThread: " + filename + " == " + fileExists );
Thread.sleep( 300 );
}
}
catch ( InterruptedException e )
{
e.printStackTrace();
return;
}
}
} );
t.start();
try
{
new WatchDir( "subdir\\" ).run();
}
catch ( IOException e )
{
e.printStackTrace();
}
}
}
我的测试用例的输出是这样的:
1. FileExistsThread: subdir\test.txt == false
2. watch registered for dir: subdir\
3. FileExistsThread: subdir\test.txt == false
4. FileExistsThread: subdir\test.txt == false
5. FileExistsThread: subdir\test.txt == false
6. FileExistsThread: subdir\test.txt == false
7. FileExistsThread: subdir\test.txt == false
8. FileExistsThread: subdir\test.txt == false
9. WatchDir event: ENTRY_CREATE: test.txt
10. WatchDir: subdir\test.txt exists == false
11. FileExistsThread: subdir\test.txt == false
12. FileExistsThread: subdir\test.txt == false
13. FileExistsThread: subdir\test.txt == false
14. FileExistsThread: subdir\test.txt == false
15. FileExistsThread: subdir\test.txt == false
16. FileExistsThread: subdir\test.txt == false
17. FileExistsThread: subdir\test.txt == false
18. FileExistsThread: subdir\test.txt == false
19. FileExistsThread: subdir\test.txt == false
20. FileExistsThread: subdir\test.txt == true
21. FileExistsThread: subdir\test.txt == true
22. FileExistsThread: subdir\test.txt == true
23. FileExistsThread: subdir\test.txt == true
如您所见,文件 test.txt 是在第 9 行创建的。watchService 已通知它。但实际上应用程序无法使用它:FileExistsThread 在第 20 行看到了它(至少 10 x 300 毫秒后)。
有任何想法吗?
[编辑]
我也试过这个:
public synchronized static boolean fileExists( final String fileName )
{
File f = new File( fileName );
String fullFileName = f.getAbsolutePath();
final String shortfileName = f.getName();
File dir = new File( fullFileName ).getParentFile();
if ( dir == null )
return false;
String[] list = dir.list( new FilenameFilter()
{
@Override
public boolean accept( File dir, String name )
{
return name.equalsIgnoreCase( shortfileName );
}
} );
if ( list == null )
return false;
return list.length == 1;
}
此方法在我预期的时候返回 true。但我仍然无法使用它之后的文件。我实际上想打开一个新的 FileInputStream(file)。这将引发 FileNotFoundExeption,而此 fileExists() 方法返回 true。现在我等到 new File().exists() 在打开 FileInputStream 之前返回 true。这行得通。
谢谢