65.9K
CodeProject 正在变化。 阅读更多。
Home

WebOpenFileDialog

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (3投票s)

2017年6月22日

CPOL

6分钟阅读

viewsIcon

6679

downloadIcon

281

本文介绍了一个适用于 Web 的 OpenFileDialog

1. 摘要 目录

这篇简短的文章介绍了一个类,它使用户能够从 Web URL 上找到的文件列表中选择文件。它还允许遍历网站的目录结构。

 

2. 背景 目录

在最近的一个项目中,我发现我需要 OpenFileDialog [^] 的功能,但用于 Web 目录。在搜索网上时,我发现了一些可以执行此功能的工具,但没有一个是我能直接放入应用程序的,尤其是使用 .Net Framework Version 3.5 开发的应用程序。

由于最终的工具可能会在我最初的项目之外有用,因此我在此介绍它。

关于排版说明

在以下讨论中,由开发者指定的属性显示为

  • 黑色粗体
  • 红色粗体是必需的
  • 红色在某些情况下是必需的

软件内部使用的变量显示为 斜体 文本。

3. WebOpenFileDialog 目录

WebOpenFileDialog 是一个派生自 .NET Form [^] 类并提供从网站上选择文件功能的工具。它包含自己的 GUI,一旦成功初始化,允许用户从 TreeView [^] 中选择一个节点,并通过“确定”按钮指示所选节点是所需文件。

3.1. 属性 目录

以下属性可以指定。

属性获取/设置默认值描述
Anonymous_FTPSetfalse如果为 true,则尝试建立匿名 FTP 连接;否则,必须提供 User_Name 和 Password。
File_NameGet如果返回时值为 DialogResult.OK,则包含用户选择的文件的完全限定文件名;否则,如果返回时值为 DialogResult.Cancel,则包含一个空字符串。
FTP_URLSetFTP 网站的 URL,从该网站检索初始目录源(即,要检索的最高目录列表)。类似于 OpenFileDialog 中的“InitialDirectory”。提供的 URL 必须符合 FTP 架构(即,它必须以 'ftp://' 或 'ftps://' 开头)。
Pass_WordSetFTP_URL 中指定的站点的 FTP 密码。如果 Anonymous_FTP 为 true,此属性可以为空字符串或站点的匿名 FTP 密码。如果 Anonymous_FTP 为 false,则必须提供站点的 FTP 密码。
Perform_PingSettrue如果为 true,则尝试使用 Ping 与网站建立连接;否则,不执行 Ping。
Show_HiddenSetfalse如果为 true,则显示以句点开头的目录条目;否则,将忽略以句点开头的目录条目。
User_NameSetFTP_URL 中指定的站点的 FTP 用户名。如果 Anonymous_FTP 为 true,此属性可以为空字符串或站点的匿名 FTP 用户名。如果 Anonymous_FTP 为 false,则必须提供站点的 FTP 用户名。
Window_TitleSet对话框标题栏中显示的标题。如果未提供,将使用“Web Open File Dialog”作为标题。

3.2. 调用 目录

using System;
using System.Text;
using System.Windows.Forms;

using WOFD = WebOpenFileDialog.WebOpenFileDialog;
:
:
    WOFD wofd = new WOFD 
        {
        Anonymous_FTP = <(true|false)>,
        FTP_URL = <URL of the FTP web site topmost node>,
        User_Name = <username for the FTP web site>,
        Pass_Word = <password for the FTP web site>,
        Perform_Ping = <(true|false)>,
        Show_Hidden = <(true|false)>,
        Window_Title = <title for the WebOpenFileDialog form>
        };

    if ( wofd.ShowDialog ( ) == DialogResult.OK )
        {
        filename_TextBox.Text = wofd.File_Name;
        }

4. 实现 目录

4.1. directories_and_files_to_treeview 目录

实际检索和展开 FTP 目录列表的方法称为 directories_and_files_to_treeview。其签名是

bool directories_and_files_to_treeview (     
                            string             ftp_url, 
                            NetworkCredential  credential,
                        ref TreeView           tree_view, 
                        ref string             error_message )

在处理 ftp_url 之前,该方法会确定 ftp_url 是否是现有 TreeView 的根节点。如果是,则会创建一个新的 TreeNode [^];否则,会搜索名为 ftp_url 的节点。这两种情况都会为 directories_and_files_to_treeview 的当前调用建立根节点。

    ftp_url = ftp_url.Replace ( @"\", "/" );

    if ( tree_view.Nodes.Count == 0 )
        {
        tree_node = new TreeNode ( ftp_url )
            {
            Tag = "DIR"
            };
        tree_view.Nodes.Add ( tree_node );
        root_node = tree_view.Nodes [ 0 ];
        }
    else 
        {
        root_node = find_tree_node ( tree_view.Nodes [ 0 ], 
                                     ftp_url ) ;
        if ( root_node == null )
            {
            error_message = String.Format (
                "Unable to locate {0}\n" +
                "in existing directory tree",
                ftp_url );
            successful = false;
            return ( successful );
            }
        }

请注意,每次调用 directories_and_files_to_treeview 时,只处理当前目录(在 ftp_url 中指定)。需要多次调用才能遍历 ftp_url 下方的目录树。此限制是强制的,以避免大量网络流量,因为假设只需要访问有限数量的子目录。

通过使用 FtpWebRequest [^] 类,该类由 WebRequest.Create [^] 返回并强制转换为 FtpWebRequest,来完成对网站目录的检索。为了获取网站上的文件目录,将请求 Method 设置为 WebRequestMethods.Ftp.ListDirectoryDetails [^]。此协议检索 FTP 服务器上文件的详细列表。初始化代码可能如下所示:

    FtpWebRequest   request;

    ftp_uri = new Uri ( ftp_url );
                            // retrieve directory details
    request = 
        ( FtpWebRequest ) WebRequest.Create ( ftp_uri );
    request.Credentials = credential;
                            // use FTP LIST to retrieve 
                            // all directory entries 
    request.Method = WebRequestMethods.
                     Ftp.ListDirectoryDetails;
    using ( WebResponse response = request.
                                   GetResponse ( ) )
        {
        using ( StreamReader stream_reader = 
                             new StreamReader ( 
                                 response.
                                 GetResponseStream ( ) ) )
            {
                            // process each line in the 
                            // directory list
            while ( stream_reader.Peek ( ) > 0 )
                {
                string  line = stream_reader.ReadLine ( );
                :
                :

目录列表由行组成,第一字符区分条目类型。 Unix 文件类型 [^] 描述了 Unix 操作系统中文件的类型。目录列表的示例是

drwx--x--x 24 amvetspost amvetspost  4096 May  1 09:31 ..
drwx------  2 amvetspost amvetspost  4096 Jul 14  2016 .HttpRequest
drwx------  4 amvetspost amvetspost  4096 Jul 14  2016 .MirrorSearch
-rw-r--r--  1 amvetspost amvetspost   100 May 22  2016 .bash_logout
-rw-r--r--  1 amvetspost amvetspost   230 May 22  2016 .bash_profile
-rw-r--r--  1 amvetspost amvetspost   176 Jul 27  2015 .bash_profile.rpmnew
-rw-r--r--  1 amvetspost amvetspost   124 Sep 22  2015 .bashrc
drwxrwx--x  5 amvetspost amvetspost  4096 Jul 14  2016 .cagefs
drwxr-xr-x  2 amvetspost amvetspost  4096 Apr 26 15:17 .cl.selector
-rw-------  1 amvetspost amvetspost     0 Jul 10  2016 .contactemail
drwxr-xr-x  3 amvetspost amvetspost  4096 Jul 14  2016 .cpan
drwx------  5 amvetspost amvetspost  4096 Apr 29 08:36 .cpanel
drwx------  3 amvetspost amvetspost  4096 Jul 14  2016 .cpcpan
drwx------  4 amvetspost amvetspost  4096 Aug 17  2016 .cphorde
drwx------  4 amvetspost amvetspost  4096 Jul 14  2016 .cpobjcache
-rw-r--r--  1 amvetspost amvetspost   145 Jul 10  2016 .gemrc
drwxr-x---  2 amvetspost 99          4096 Jul 10  2016 .htpasswds
-rw-------  1 amvetspost amvetspost   650 Apr 29 08:36 .lastlogin
-rw-r--r--  1 amvetspost amvetspost    33 May 22  2016 .md5sum
drwxr-xr-x  2 0          0           4096 Jun 19 12:03 .mysql_backup
drwx------  2 amvetspost amvetspost  4096 Apr 26 15:58 .subaccounts
drwx------  2 amvetspost amvetspost  4096 Jul 15  2016 .trash
-rw-r--r--  1 amvetspost amvetspost   658 Mar 25  2016 .zshrc
drwxr-xr-x  2 amvetspost amvetspost  4096 Jun 13 08:51 AmVetsData
lrwxrwxrwx  1 amvetspost amvetspost    39 Jul 10  2016 access-logs -> /usr/local/apache/domlogs/amvetspost292
drwxr-xr-x  2 amvetspost amvetspost  4096 Aug 17  2016 cache
drwxr-x---  2 amvetspost 12          4096 Jul 10  2016 etc
drwx------  2 amvetspost amvetspost  4096 Jun 14 05:20 logs
drwxr-x---  8 amvetspost amvetspost  4096 Jul 14  2016 mail

每行第一个字符决定是否处理。出于我们的目的,只保留普通文件(第一个字符为连字符 (-))和目录(第一个字符为小写字母 'd')。所有其他将忽略。

如果目录条目名(目录列表中的最后一列)以句点开头,则该条目被视为“隐藏”条目。除非将 Show_Hidden 属性设置为 true,否则将忽略隐藏条目。如果目录列表行的第一个字符是 'd' 且目录条目名以句点结尾,则忽略该条目。后者消除了目录 '.'(当前目录)和 '..'(父目录)。

有了这些第一个字符的定义,代码就变成了

                char    first_ch = ( char ) 0;
                int     index = 0;
                string  name = String.Empty;
                string  type = String.Empty;

                first_ch = line [ 0 ];
                if ( first_ch == 'd' )
                    {
                    if ( line.EndsWith ( "." ) )
                        {
                        continue;   // ignore . and ..
                        }
                    type = "DIR";
                    }
                else if ( first_ch == '-' ) 
                    {
                    type = "FILE";
                    }
                else 
                    {
                    continue;       // ignore all others
                    }

                index = line.LastIndexOf ( ' ' );
                if ( index < 0 )
                    {
                    continue;
                    }

                name = line.Substring ( index ).Trim ( );
                if ( name.StartsWith ( "." ) )
                    {
                    if ( !Show_Hidden )
                        {
                        continue;
                        }
                    }

                tree_node = new TreeNode ( name )
                    {
                    Tag = type
                    };
                if ( type.Equals ( "DIR" ) )
                    {
                    tree_node.Nodes.Add ( 
                        new TreeNode ( "EMPTY" )
                            {
                            Tag = "EMPTY"
                            } );
                    }

                root_node.Nodes.Add ( tree_node );
                }

目录列表中的每个非忽略的目录条目名都分配给一个 TreeNode,其类型为“DIR”或“FILE”,并存储在 Tag 中。如果要添加一个新的目录节点(DIR),它会被赋予一个类型为“EMPTY”的子节点,然后再添加到 TreeView 中。这个空节点会导致 TreeView 控件在节点前放置一个加号 (+),从而暗示存在一个子树。请注意,TreeNode 的展开仅在该 TreeNode 被作为根处理时(即,在调用 directories_and_files_to_treeview 时作为 ftp_url 传递)发生。

4.2. find_tree_node 目录

TreeNode 搜索方法可能很有趣。其内容是

TreeNode find_tree_node ( TreeNode root, 
                          string   name_to_find ) 
    {
    Stack < TreeNode >  stack = new Stack < TreeNode > ( );
    bool                no_strip = false;

    stack.Push ( root );
    name_to_find = name_to_find.Replace ( @"\", "/" );
    no_strip = name_to_find.EndsWith ( "/" );
    while ( stack.Count > 0 )
        {
        StringBuilder   name = new StringBuilder ( );
        TreeNode        node = ( TreeNode ) stack.Pop ( );
        
        name.AppendFormat ( "{0}/{1}",
                            node.FullPath,
                            node.Name );
        name.Replace ( @"\", "/" );
        if ( !no_strip )
            {
            while ( name.ToString ( ).EndsWith ( "/" ) )
                {
                name.Length--;
                }
            }

        if ( name.ToString ( ).Equals ( name_to_find ) )
            {
            return ( node );
            }

        foreach ( TreeNode child in node.Nodes )
            {
            if ( child != null )
                {
                stack.Push( child );
                }
            }
        }

    return ( null );
    }

给定一个用于搜索的根节点以及要搜索的节点名称,find_tree_node 会在 root 的子树中查找具有 name_to_find 名称的节点。如果搜索成功,则返回包含该名称的 TreeNode;否则,返回 null。

我曾考虑使用递归搜索,但迭代方法似乎更简单。因此,简单胜过了优雅!

5. 使用 WebOpenFileDialog 目录

根据构建,WebOpenFileDialog 是一个包含 WebOpenFileDialog 类的项目,该类派生自 Form 类。它具有 WebOpenFileDialog 命名空间。要在 Microsoft Visual Studio [^] 中使用此类,只需将 WebOpenFileDialog 项目复制到包含的解决方案中。

6. 演示 目录

演示项目已包含在下载文件中。执行后,它会显示

选择文件后(如 第一个图 所示)并点击“确定”按钮,所选文件名的完全限定路径将出现在 Filename TextBox 中。

7. 参考 目录

8. 开发环境 目录

WebOpenFileDialog 是在以下环境中开发的

Microsoft Windows 7 Professional Service Pack 1
Microsoft Visual Studio 2008 Professional
Microsoft .Net Framework Version 3.5 SP1
Microsoft Visual C# 2008

9. 历史 目录

06/27/2017     原文
© . All rights reserved.