在这篇相关文章How to navigate to a network host in JFileChooser?中,提出了一个不错的解决方案,使用ShellFolder(sun private API)设置JFileChooser的当前目录。我们已经使用这种方法进行了一些修改,并且在过去几年中没有出现任何问题。
public static File getNetworkShareFolder( final File folder ) throws IllegalArgumentException {
final File file = new NonCanonicalizingFile( folder.getPath() );
if( isNetworkShareFolder( file ) ) { // assume Win32ShellFolderManager2 will be present
try {
// JRE-13272 -PRIVATE API that must eventually be swapped for Java 9 alternative
// Using reflection because Win32ShellFolderManager2 may not exist in rt.jar on Linux build server
final Class win32ShellMgr = Class.forName( "sun.awt.shell.Win32ShellFolderManager2" );
// get static creation method from class, execute it
final Method cfMethod = win32ShellMgr.getMethod( "createShellFolder", File.class );
return (ShellFolder) cfMethod.invoke( win32ShellMgr.newInstance(), file );
} catch( final Exception xx ) {
xx.printStackTrace();
}
}
throw new IllegalArgumentException( "Given path is not a Windows network share folder." );
}
然而,我们正在迁移到Java 11,在Java 9及以上版本中,私有API已被封装,不再允许使用。不用担心,OpenJDK中的替代API已经进入了FileSystemView中,是swing文件选择器的一个子包。
sun.awt.shell.ShellFolder.isComputerNode( File ) -> javax.swing.filechooser.FileSystemView.getFileSystemView().isComputerNode( File ) sun.awt.shell.ShellFolder.getShellFolder( File ) -> javax.swing.filechooser.FileSystemView.getFileSystemView().getLinkLocation( File )
所以之前的代码现在变成了:
public static File getNetworkShareFolder( final File folder ) throws IllegalArgumentException {
final File file = new NonCanonicalizingFile( folder.getPath() );
if( isNetworkShareFolder( file ) ) {
try {
return FileSystemView.getFileSystemView().getLinkLocation( file );
} catch( final Exception xx ) {
xx.printStackTrace();
}
}
throw new IllegalArgumentException( "Given path is not a Windows network share folder." );
}
public static boolean isNetworkShareFolder( final File folder ) {
return FileSystemView.getFileSystemView().isComputerNode( new NonCanonicalizingFile( folder.getPath() ) );
}
这很好,但不幸的是,在Java 11下,getShellFolder()和getLinkLocation()都会抛出一个在Java 8下没有抛出的异常。
java.nio.file.InvalidPathException: UNC路径缺少共享名称: \100.212.51.37 在 java.base/sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:118) 在 java.base/sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:77) 在 java.base/sun.nio.fs.WindowsPath.parse(WindowsPath.java:92) 在 java.base/sun.nio.fs.WindowsFileSystem.getPath(WindowsFileSystem.java:229) 在 java.base/java.nio.file.Path.of(Path.java:147) 在 java.base/java.nio.file.Paths.get(Paths.java:69) 在 java.desktop/sun.awt.shell.ShellFolder.getShellFolder(ShellFolder.java:247) 在 java.desktop/javax.swing.filechooser.FileSystemView.getLinkLocation(FileSystemView.java:641)
现在它认为除非有实际的共享名称,否则UNC路径无效,例如"\\100.212.51.37\"是无效的,但"\\100.212.51.37\myShare"是可以的。
如果你获取UNC路径"\\100.212.51.37\myShare"的shell文件夹,然后getParent(),你就可以得到我们最初想要的"\\100.212.51.37\"的shell文件夹。不幸的是,这个解决方法对我们的客户来说不可行,因为存在鸡生蛋的问题 - 用户通常还不知道任何实际的共享名称,这正是他们想要浏览的内容!
哎呀 - 这在Java 8下很有效,但在Java 11下,即使你打破封装使用原始的ShellFolder私有API,也不起作用。
'--add-exports', 'java.desktop/sun.awt.shell=ALL-UNNAMED'
前面提到的解决方案在Java 11(9+)中也会抛出相同的异常,因此并没有帮助。
另一个解决方案是使用JCIFS中的SmbFile类,但由于安全限制,我们很难使用第三方代码,特别是如果它没有更新为Java 11 JPMS并且不使用私有API。
有趣的是,在JavaFX中的DirectoryChooser没有这个问题。如果用户手动输入网络主机名,它将愉快地显示该主机的所有共享名称。如果必须这样做,我们就会走这条路。但是,在Swing应用程序上处理FX Stage之间的模态性非常麻烦,而且可能需要大量的工作。
仍然希望找到更简单的解决方法,在Java 11(Java 9+)中让JFileChooser显示网络共享!也许有人知道FX DirectoryChooser使用的技巧,可以将其应用到JFileChooser上?