java - 如何在 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?
解决方案
仍在寻找更好的解决方案,但与此同时,由于网络共享,我们决定在 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 才尊重“暗模式”设置,而我们的客户几乎没有。
推荐阅读
- python - 将数据框的字典写入excel文件会产生错误
- python - 计算精度(在 lstm 模型中)
- .net - Sendgrid SendEmailAsync 响应内容始终为空
- python - sklearn FeatureHasher 输出有很多冲突和未使用的列
- javascript - VSCODE 在尝试调试 javascript 时出错:“vscode listen eacces: permission denied 127.0.0.1:53942”
- shell - AWS CLI 命令未使用 Bash 脚本设置 ENV 变量
- android - 如何将 Retrofit 中的信息实现到 viewPager2 布局小部件中
- node.js - 上传图片到云端的问题
- python - Pypy Pandas 没有安装
- touch - 如何在 MicroPython 中的 micro:bit v2 上将 Pin 触摸模式设置为电容式