首页 > 解决方案 > 如何在 Java 9+ 中使用 JFileChooser 显示网络共享?

问题描述

我们软件的用户需要在我们的 Java swing 应用程序中浏览 Windows 10 上的网络共享,但是 swing 的 JFileChooser 默认不具备此功能。

在这篇相关文章 中,如何在 JFileChooser 中导航到网络主机? 一个不错的解决方案是使用 ShellFolder(sun 私有 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 filechooser 的子包中。

sun.awt.shell.ShellFolder.isComputerNode(文件)-> javax.swing.filechooser.FileSystemView.getFileSystemView().isComputerNode(文件) sun.awt.shell.ShellFolder.getShellFolder(文件)-> javax.swing.filechooser。 FileSystemView.getFileSystemView().getLinkLocation( 文件 )

所以之前的代码现在变成了

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() ) );
}

这会很棒,但不幸的是 getShellFolder() 和 getLinkLocation() 在 Java 11 下都抛出了一个在 Java 8 下没有抛出的异常。

java.nio.file.InvalidPathException:UNC 路径缺少共享名:\100.212.51.37 at java.base/sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:118) at 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 文件夹。不幸的是,由于鸡和蛋的问题,这种解决方法对我们的客户不可行 - 用户通常还不知道任何实际的共享名称,这是他们首先想要浏览的内容!

Argh - 这在 Java 8 下运行良好,但在 Java 11 中,即使您打破封装以使用原始的 ShellFolder 私有 API

'--add-exports', 'java.desktop/sun.awt.shell=ALL-UNNAMED'

它没有帮助,因为之前的解决方案现在在 Java 11 (9+) 下抛出了相同的异常。

我们在 StackOverflow 上看到的另一个解决方案是使用 JCIFS 中的 SmbFile 类但由于安全限制,我们很难使用 3rd 方代码。特别是如果它没有针对不使用私有 API 的 Java 11 JPMS 进行更新。

有趣的是,JavaFX 中的 DirectoryChooser 没有这个问题。如果用户手动输入网络主机,它会很乐意显示该主机的所有共享名称。如果必须,我们将采用这种方式,但处理 FX Stage 与 Swing 应用程序之间的模态是丑陋的,并且可能需要大量工作。

仍然希望有一个更简单的解决方法来让 JFileChooser 在 Java 11 (Java 9+) 中显示网络共享!也许有人知道 FX DirectoryChooser 使用的技巧,它可以应用于 JFileChooser?

标签: javawindowsswingjfilechoosernetwork-share

解决方案


仍在寻找更好的解决方案,但与此同时,由于网络共享,我们决定在 Java 11 中用 JavaFX 的 DirectoryChooser 替换 JFileChooser。DirectoryChooser 非常乐意允许用户输入根网络共享,它将显示所有网络共享名称。启动 DirectoryChooser 的 UI 仍然是一个摆动 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 才尊重“暗模式”设置,而我们的客户几乎没有。


推荐阅读