如何在Java 9+中使用JFileChooser显示网络共享?

9
我们的软件用户需要在我们的Java Swing应用程序中浏览Windows 10上的网络共享,然而Swing的JFileChooser默认情况下不具备此功能。
在这篇相关文章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上?

1个回答

1
在寻找更好的解决方案的同时,我们决定在Java 11中用JavaFX的DirectoryChooser替换JFileChooser,因为涉及到网络共享。DirectoryChooser很乐意让用户输入根网络共享,并显示所有网络共享名称。启动DirectoryChooser的UI仍然是Swing UI,但至少在Windows 10下,我们没有遇到模态或焦点问题。
然而,对于我们来说,存在重大缺点,除了需要仔细管理JavaFX Platform.runLater()和SwingUtilities.invokeLater()之间的线程:
1)DirectoryChooser不允许多选。JFileChooser可以。
2)DirectoryChooser仅允许选择目录而不是文件。耸肩谁需要允许选择目录或文件?愚蠢。但这正是我们的客户在多个实例中需要做的。JFileChooser支持此功能。

3) DirectoryChooser不允许输入非有效路径。什么?是的,实际上,我们的其中一个客户有一个特定的要求,即输入尚不存在的路径(预配置),但一旦任务驱动器连接到网络,它就会存在了。JFileChooser允许这样做,可以非常方便地导航到所需的根目录,然后只需键入路径的最后一部分(目录或共享名称)。

4) DirectoryChooser与应用程序风格不匹配。在JavaFX中,您可以在场景的根节点上设置自己的CSS,但不能在DirectoryChooser上进行设置。看起来DirectoryChooser实际上是调用本机文件选择器,应该使用系统颜色方案。不幸的是,对于使用“暗模式”操作的我们的飞行员,在Windows 10文件资源管理器中直到1809版本之前都没有考虑“暗模式”设置,而几乎没有我们的客户有这个版本。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接