本文介绍了如何导航到JFileChooser中的网络主机?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题

我有一个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:无效参数
      在java.io.WinNTFileSystem .Cononicalize0(Native Method)
      at java.io.WinNTFileSystem.canonicalize(WinNTFileSystem.java:428)
      at java.io.File.getCanonicalPath(File.java:618)
      at java 。
      File dir = ShellFolder.getShellFolder(new File(\\\\\blah));



      失败:

       在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)
      \\ 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, and new 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 call File.getCanonicalPath() on the given file, which will in turn call WinNTFileSystem.canonicalize(). This last call always fails on computer paths. After much experimentation, I was able to create a ShellFolder for any accessible computer path by wrapping the File object in something that bypasses WinNTFileSystem.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 its approveSelection() 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 its createFileObject(String) method to check for computer paths. This allows passing a computer path to JFileChooser(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 overriding JFileChooser.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中的网络主机?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-20 07:05