如何在 Java 9+ 中使用 JFileChooser 显示网络共享?
Posted
技术标签:
【中文标题】如何在 Java 9+ 中使用 JFileChooser 显示网络共享?【英文标题】:How to display network shares using JFileChooser in Java 9+? 【发布时间】:2019-04-10 09:42:44 【问题描述】:我们软件的用户需要在我们的 Java swing 应用程序中浏览 Windows 10 上的网络共享,但是 swing 的 JFileChooser 默认不具备此功能。
在此相关帖子中 How to navigate to a network host in 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 在 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 文件夹.不幸的是,这种解决方法对于我们的客户来说是不可行的,因为存在先有鸡还是先有蛋的问题 - 用户通常还不知道任何实际的共享名称,这是他们首先想要浏览的内容!
Argh - 这在 Java 8 下运行良好,但在 Java 11 中,即使您打破封装以使用原始的 ShellFolder 私有 API,使用
'--add-exports', 'java.desktop/sun.awt.shell=ALL-UNNAMED'
这没有帮助,因为以前的解决方案现在在 Java 11 (9+) 下抛出相同的异常。
我们在 *** 上看到的另一个解决方案是使用 JCIFS 中的 SmbFile 类,但由于安全限制,我们很难使用 3rd 方代码。特别是如果它没有针对 Java 11 JPMS 进行更新,并且不使用私有 API。
有趣的是,JavaFX 中的 DirectoryChooser 没有这个问题。如果用户手动输入网络主机,它会很乐意显示该主机的所有共享名称。如果必须,我们将采用这种方式,但处理 FX Stage 与 Swing 应用程序之间的模态是丑陋的,并且可能需要大量工作。
仍然希望有一个更简单的解决方法,让 JFileChooser 在 Java 11(Java 9+)中显示网络共享!也许有人知道 FX DirectoryChooser 使用的技巧,它可以应用于 JFileChooser?
【问题讨论】:
【参考方案1】:仍在寻找更好的解决方案,但与此同时,由于网络共享,我们决定在 Java 11 中将 JFileChooser 替换为 JavaFX 的 DirectoryChooser。 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 才尊重“暗模式”设置,而我们的客户几乎没有。
【讨论】:
以上是关于如何在 Java 9+ 中使用 JFileChooser 显示网络共享?的主要内容,如果未能解决你的问题,请参考以下文章
如何(不)在Java 9+中使用Reactive Streams