问题描述
问题我有一个JFileChooser,我需要以编程方式将currentDirectory设置为包含多个SMB共享的网络主机(例如 \ \blah
)。从技术上讲,这不是一个目录,而是一个表示可用共享列表的shell文件夹。
-
JFileChooser在浏览时没有问题(例如
\\blah\someShare
),但不能处理主机目录本身(例如\\blah
- 用户可以通过Networkshell文件夹找到JFileChooser内的目录特定的共享和导航到其父目录。调试显示这个目录下的这个目录被表示为一个
Win32ShellFolder2
。
-
新建文件(\\\\\\ blah)我们可以通过编程来设置currentDirectory的所有尝试都失败了。
)可以被创建,但是从Java的角度来看并不存在。 -
chooser.setCurrentDirectory(new File(\\\\blah ));
因为
JFileChooser
检查给定的目录是否存在, code> new File(\\\\\blah)。exists()返回false。 -
失败,例外:
java.io.IOException:无效参数
File dir = ShellFolder.getShellFolder(new File(\\\\\blah));
在java.io.WinNTFileSystem .Cononicalize0(Native Method)
at java.io.WinNTFileSystem.canonicalize(WinNTFileSystem.java:428)
at java.io.File.getCanonicalPath(File.java:618)
at java 。
失败:
在sun.awt.shell.ShellFolder.get )
-
File dir = new Win32ShellFolderManager2()。createShellFolder文件(\\\\\blah));
失败,例外:
java.io.FileNotFoundException:File \\blah not found
at sun.awt.shell.Win32ShellFolderManager2.createShellFolder(Win32ShellFolderManager2.java: 80)
at sun.awt.shell.Win32ShellFolderManager2.createShellFolder(Win32ShellFolderManager2.java:64)
-
Path dir = Paths.get(\\\\\blah);
失败,例外:
<$在Sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:118)中,缺少共享名:\\bla
。
at sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:77)
at sun.nio.fs.WindowsPath.parse(WindowsPath.java:94)
at sun.nio .fs.WindowsFileSystem.getPath(WindowsFileSystem.java:255)
位于java.nio.file.Paths.get(Paths.java:84)
$ p $我发现了一个特定于Windows的解决方案,可以导航到任何可访问的计算机节点从它的名字(例如\\ blah
或\\blah\
),而无需枚举Network shell文件夹,或给定节点上网络共享的任何预先知识。
为计算机路径创建ShellFolder
while调试这个问题我发现一个
ShellFolder
必须为给定的计算机路径创建才能导航到它。Win32ShellFolderManager2.createShellFolder()
会在给定文件中调用File.getCanonicalPath()
,然后调用WinNTFileSystem.canonicalize()
。计算机路径上最后一次调用总是失败。经过大量的实验,我可以通过绕过WinNTFileSystem.canonicalize()来绕过File对象,为任何可访问的计算机路径创建一个
ShellFolder
/ code>:
/ **
*为给定网络路径创建一个shell文件夹。
*
* @param路径文件来测试是否存在。
*返回表示给定计算机节点的ShellFolder。
* @throws IllegalArgumentException给定的路径不是一个计算机节点。
* @throws FileNotFoundException找不到路径。
* /
public static ShellFolder getComputerNodeFolder(String path)
抛出FileNotFoundException {
File file = new NonCanonicalizingFile(path);
if(ShellFolder.isComputerNode(file)){
return new Win32ShellFolderManager2()。createShellFolder(file);
} else {
throw new IllegalArgumentException(给定路径不是计算机节点);
private static final class NonCanonicalizingFile extends File {
public NonCanonicalizingFile(String path){
super(path);
$ b @Override
public String getCanonicalPath()throws IOException {
// Win32ShellFolderManager2.createShellFolder()将在这个文件上调用getCanonicalPath()。
// getCanonicalPath()的基本实现调用WinNTFileSystem.canonicalize(),它在
//计算机节点(例如\\blah)上失败。我们跳过canonicalize调用,在这一点上是安全的,因为我们有
//确认(在approveSelection()中)这个文件代表一个计算机节点。
return getAbsolutePath(); (b
无可否认,这个解决方案有一些边缘情况
\\blah\
可以工作,但\\blah\someShare\\\\
确实不),理想情况下OpenJDK应该修复这些怪癖。集成JFileChooser:选项1
$ b $ p与
JFileChooser
集成的最简单方法是覆盖它的approveSelection()
方法。这允许用户键入一个计算机路径(\\ blah
或\\blah\
)在对话框中按Enter键在那里导航。当给出不存在或不可访问的路径时,将显示一条警告消息。JFileChooser选择器= new JFileChooser(){
@Override
public void approveSelection(){
File selectedFile = getSelectedFile();
if(selectedFile!= null&& ShellFolder.isComputerNode(selectedFile)){
try {
//解析路径并尝试导航到它
setCurrentDirectory(getComputerNodeFolder( selectedFile.getPath()));
} catch(FileNotFoundException ex){
//如果给定的计算机节点无法访问,则提醒用户
JOptionPane.showMessageDialog(this,Can not access+ selectedFile.getPath());
}
} else {
super.approveSelection();
}
}
};
chooser.showOpenDialog(null);
$ / $ p
与JFileChooser集成:选项2
或者,通过覆盖它的
createFileObject(String)
方法来检查计算机路径,可以增加FileSystemView
。这允许将计算机路径传递给JFileChooser(String,FileSystemView)
构造函数,并允许用户导航到可访问的计算机路径。但是,仍然没有简单的方法可以告诉用户有关不可访问的计算机路径,而不必重写JFileChooser.approveSelection()
:
$ b $公共静态类ComputerNodeFriendlyFileSystemView扩展FileSystemView {
private final FileSystemView委托;
public ComputerNodeFriendlyFileSystemView(FileSystemView委托){
this.delegate = delegate;
}
@Override
public File createFileObject(String path){
文件placeholderFile = new File(path);
if(ShellFolder.isComputerNode(placeholderFile)){
try {
return getComputerNodeFolder(path);
} catch(FileNotFoundException ex){
return placeholderFile;
}
} else {
return delegate.createFileObject(path);
//下面的所有代码只需将所有内容委托给委托
$ b $覆盖
public File createNewFolder(File containsDir)抛出IOException {
return delegate.createNewFolder(containedDir);
}
@Override
public boolean isRoot(File f){
return delegate.isRoot(f);
}
@Override
public Boolean isTraversable(File f){
return delegate.isTraversable(f);
}
@Override
public String getSystemDisplayName(File f){
return delegate.getSystemDisplayName(f);
}
@Override
public String getSystemTypeDescription(File f){
return delegate.getSystemTypeDescription(f);
}
@Override
public Icon getSystemIcon(File f){
return delegate.getSystemIcon(f);
$ b @Override
public boolean isParent(File folder,File file){
return delegate.isParent(folder,file);
$ b @Override
public File getChild(File parent,String fileName){
return delegate.getChild(parent,fileName);
$ b @Override
public boolean isFileSystem(File f){
return delegate.isFileSystem(f);
$ b @Override
public boolean isHiddenFile(File f){
return delegate.isHiddenFile(f);
}
@Override
public boolean isFileSystemRoot(File dir){
return delegate.isFileSystemRoot(dir);
$ b @Override
public boolean isDrive(File dir){
return delegate.isDrive(dir);
}
@Override
public boolean isFloppyDrive(File dir){
return delegate.isFloppyDrive(dir);
}
@Override
public boolean isComputerNode(File dir){
return delegate.isComputerNode(dir);
$ b @Override
public File [] getRoots(){
return delegate.getRoots();
}
@Override
public File getHomeDirectory(){
return delegate.getHomeDirectory();
@Override
public File getDefaultDirectory(){
return delegate.getDefaultDirectory();
$ b @Override
public File createFileObject(File dir,String filename){
return delegate.createFileObject(dir,filename);
$ b @Override
public File [] getFiles(File dir,boolean useFileHiding){
return delegate.getFiles(dir,useFileHiding);
}
@Override
public File getParentDirectory(File dir){
return delegate.getParentDirectory(dir);
$ / code $ / pre
用法:
ComputerNodeFriendlyFileSystemView fsv
= new ComputerNodeFriendlyFileSystemView(FileSystemView.getFileSystemView());
JFileChooser chooser = new JFileChooser(\\\\\blah,fsv);
chooser.showOpenDialog(null);
The Problem
I have a JFileChooser and I need to programmatically set its currentDirectory to a network host containing several SMB shares (e.g.
\\blah
). Technically this is not a "directory" but rather a shell folder representing a list of available shares.JFileChooser has no problems navigating to a specific share (e.g.
\\blah\someShare
) but cannot handle the host "directory" itself (e.g.\\blah
).Users can navigate to such "directories" inside JFileChooser by going via "Network" shell folder, or by finding a specific share and navigating to its parent directory. Debugging shows that under-the-hood this directory is represented as a
Win32ShellFolder2
. All my attempts to set currentDirectory programmatically have failed so far.new File("\\\\blah")
can be created, but does not actually exist from Java's perspective.
Failed Solution Attempts
chooser.setCurrentDirectory(new File("\\\\blah"));
Fails because
JFileChooser
checks if the given directory exists, andnew File("\\\\blah").exists()
returns false.File dir = new File("\\\\blah").getCanonicalFile();
Fails with an exception:
java.io.IOException: Invalid argument at java.io.WinNTFileSystem.canonicalize0(Native Method) at java.io.WinNTFileSystem.canonicalize(WinNTFileSystem.java:428) at java.io.File.getCanonicalPath(File.java:618) at java.io.File.getCanonicalFile(File.java:643)
File dir = ShellFolder.getShellFolder(new File("\\\\blah"));
Fails with an exception:
java.io.FileNotFoundException at sun.awt.shell.ShellFolder.getShellFolder(ShellFolder.java:247)
File dir = new Win32ShellFolderManager2().createShellFolder(new File("\\\\blah"));
Fails with an exception:
java.io.FileNotFoundException: File \\blah not found at sun.awt.shell.Win32ShellFolderManager2.createShellFolder(Win32ShellFolderManager2.java:80) at sun.awt.shell.Win32ShellFolderManager2.createShellFolder(Win32ShellFolderManager2.java:64)
Path dir = Paths.get("\\\\blah");
Fails with an exception:
java.nio.file.InvalidPathException: UNC path is missing sharename: \\blah at sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:118) at sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:77) at sun.nio.fs.WindowsPath.parse(WindowsPath.java:94) at sun.nio.fs.WindowsFileSystem.getPath(WindowsFileSystem.java:255) at java.nio.file.Paths.get(Paths.java:84)
解决方案I found a Windows-specific solution that allows navigating to any accessible computer node from its name alone (e.g.
\\blah
or\\blah\
), without requiring enumeration of the Network shell folder, or any advance knowledge of network shares on the given node.Creating a ShellFolder for a computer path
While debugging this issue I discovered that a
ShellFolder
has to be created for the given computer path to be able to navigate to it.Win32ShellFolderManager2.createShellFolder()
will callFile.getCanonicalPath()
on the given file, which will in turn callWinNTFileSystem.canonicalize()
. This last call always fails on computer paths. After much experimentation, I was able to create aShellFolder
for any accessible computer path by wrapping the File object in something that bypassesWinNTFileSystem.canonicalize()
:/** * Create a shell folder for a given network path. * * @param path File to test for existence. * @return ShellFolder representing the given computer node. * @throws IllegalArgumentException given path is not a computer node. * @throws FileNotFoundException given path could not be found. */ public static ShellFolder getComputerNodeFolder(String path) throws FileNotFoundException { File file = new NonCanonicalizingFile(path); if (ShellFolder.isComputerNode(file)) { return new Win32ShellFolderManager2().createShellFolder(file); } else { throw new IllegalArgumentException("Given path is not a computer node."); } } private static final class NonCanonicalizingFile extends File { public NonCanonicalizingFile(String path) { super(path); } @Override public String getCanonicalPath() throws IOException { // Win32ShellFolderManager2.createShellFolder() will call getCanonicalPath() on this file. // Base implementation of getCanonicalPath() calls WinNTFileSystem.canonicalize() which fails on // computer nodes (e.g. "\\blah"). We skip the canonicalize call, which is safe at this point because we've // confirmed (in approveSelection()) that this file represents a computer node. return getAbsolutePath(); } }
Admittedly this solution has a couple edge-cases (e.g.
\\blah\
works but\\blah\someShare\..\
does not), and ideally OpenJDK should fix these quirks on their end. This is also an OS-specific and implementation-specific solution, and will not work outside OpenJDK-on-Windows setup.Integrating with JFileChooser: Option 1
The simplest way to integrate this with
JFileChooser
is to override itsapproveSelection()
method. This allows user to type in a computer path (\\blah
or\\blah\
) in the dialog and press Enter to navigate there. An alert message is shown when a non-existent or non-accessible path was given.JFileChooser chooser = new JFileChooser() { @Override public void approveSelection() { File selectedFile = getSelectedFile(); if (selectedFile != null && ShellFolder.isComputerNode(selectedFile)) { try { // Resolve path and try to navigate to it setCurrentDirectory(getComputerNodeFolder(selectedFile.getPath())); } catch (FileNotFoundException ex) { // Alert user if given computer node cannot be accessed JOptionPane.showMessageDialog(this, "Cannot access " + selectedFile.getPath()); } } else { super.approveSelection(); } } }; chooser.showOpenDialog(null);
Integrating with JFileChooser: Option 2
Alternatively,
FileSystemView
can be augmented by overriding itscreateFileObject(String)
method to check for computer paths. This allows passing a computer path toJFileChooser(String,FileSystemView)
constructor and still allows user to navigate to accessible computer paths. However, there is still no easy way to message the user about non-accessible computer paths without overridingJFileChooser.approveSelection()
:public static class ComputerNodeFriendlyFileSystemView extends FileSystemView { private final FileSystemView delegate; public ComputerNodeFriendlyFileSystemView(FileSystemView delegate) { this.delegate = delegate; } @Override public File createFileObject(String path) { File placeholderFile = new File(path); if (ShellFolder.isComputerNode(placeholderFile)) { try { return getComputerNodeFolder(path); } catch (FileNotFoundException ex) { return placeholderFile; } } else { return delegate.createFileObject(path); } } // All code below simply delegates everything to the "delegate" @Override public File createNewFolder(File containingDir) throws IOException { return delegate.createNewFolder(containingDir); } @Override public boolean isRoot(File f) { return delegate.isRoot(f); } @Override public Boolean isTraversable(File f) { return delegate.isTraversable(f); } @Override public String getSystemDisplayName(File f) { return delegate.getSystemDisplayName(f); } @Override public String getSystemTypeDescription(File f) { return delegate.getSystemTypeDescription(f); } @Override public Icon getSystemIcon(File f) { return delegate.getSystemIcon(f); } @Override public boolean isParent(File folder, File file) { return delegate.isParent(folder, file); } @Override public File getChild(File parent, String fileName) { return delegate.getChild(parent, fileName); } @Override public boolean isFileSystem(File f) { return delegate.isFileSystem(f); } @Override public boolean isHiddenFile(File f) { return delegate.isHiddenFile(f); } @Override public boolean isFileSystemRoot(File dir) { return delegate.isFileSystemRoot(dir); } @Override public boolean isDrive(File dir) { return delegate.isDrive(dir); } @Override public boolean isFloppyDrive(File dir) { return delegate.isFloppyDrive(dir); } @Override public boolean isComputerNode(File dir) { return delegate.isComputerNode(dir); } @Override public File[] getRoots() { return delegate.getRoots(); } @Override public File getHomeDirectory() { return delegate.getHomeDirectory(); } @Override public File getDefaultDirectory() { return delegate.getDefaultDirectory(); } @Override public File createFileObject(File dir, String filename) { return delegate.createFileObject(dir, filename); } @Override public File[] getFiles(File dir, boolean useFileHiding) { return delegate.getFiles(dir, useFileHiding); } @Override public File getParentDirectory(File dir) { return delegate.getParentDirectory(dir); } }
Usage:
ComputerNodeFriendlyFileSystemView fsv = new ComputerNodeFriendlyFileSystemView(FileSystemView.getFileSystemView()); JFileChooser chooser = new JFileChooser("\\\\blah", fsv); chooser.showOpenDialog(null);
这篇关于如何导航到JFileChooser中的网络主机?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
尝试