【问题标题】:Listing server folders and sub folders using C# FtpWebRequest使用 C# FtpWebRequest 列出服务器文件夹和子文件夹
【发布时间】:2013-04-09 14:27:08
【问题描述】:

我正在尝试使用StreamReader 在树视图上获取文件、目录和子目录的完整列表。问题是它花费了太长时间并抛出*“操作超时异常”并且只显示一个级别。

这是我的代码

public void getServerSubfolder(TreeNode tv, string parentNode) {
  string ptNode;
  List<string> files = new List<string> ();
  try { 
    FtpWebRequest request = (FtpWebRequest) WebRequest.
    Create(parentNode);
    request.Method = WebRequestMethods.Ftp.ListDirectoryDetails;

    request.Credentials = new NetworkCredential(this.userName, this.Password);
    request.UseBinary = true;
    request.UsePassive = true;
    request.Timeout = 10000;
    request.ReadWriteTimeout = 10000;
    request.KeepAlive = false;
    FtpWebResponse response = (FtpWebResponse) request.GetResponse();
    Stream responseStream = response.GetResponseStream();
    StreamReader reader = new StreamReader(responseStream);
    string fileList;
    string[] fileName;
    //MessageBox.Show(reader.ReadToEnd().ToString());
    while (!reader.EndOfStream) {
      fileList = reader.ReadLine();
      fileName = fileList.Split(' ');
      if (fileName[0] == "drwxr-xr-x") {
        // if it is directory
        TreeNode tnS = new TreeNode(fileName[fileName.Length - 1]);
        tv.Nodes.Add(tnS);
        ptNode = parentNode + "/" + fileName[fileName.Length - 1] + "/";
        getServerSubfolder(tnS, ptNode);
      } else files.Add(fileName[fileName.Length - 1]);
    }
    reader.Close();
    response.Close();
  } catch (Exception ex) {
    MessageBox.Show("Sub--here " + ex.Message + "----" + ex.StackTrace);
  }
}

【问题讨论】:

  • 我尝试过不同的超时数字。

标签: c# .net ftp streamreader ftpwebrequest


【解决方案1】:

您必须在递归到子目录之前读取(并缓存)整个列表,否则顶级请求将在您完成子目录列表之前超时。

您可以继续使用ReadLine,无需使用ReadToEnd并自行分隔行。

void ListFtpDirectory(
    string url, string rootPath, NetworkCredential credentials, List<string> list)
{
    FtpWebRequest listRequest = (FtpWebRequest)WebRequest.Create(url + rootPath);
    listRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
    listRequest.Credentials = credentials;

    List<string> lines = new List<string>();

    using (FtpWebResponse listResponse = (FtpWebResponse)listRequest.GetResponse())
    using (Stream listStream = listResponse.GetResponseStream())
    using (StreamReader listReader = new StreamReader(listStream))
    {
        while (!listReader.EndOfStream)
        {
            lines.Add(listReader.ReadLine());
        }
    }

    foreach (string line in lines)
    {
        string[] tokens =
            line.Split(new[] { ' ' }, 9, StringSplitOptions.RemoveEmptyEntries);
        string name = tokens[8];
        string permissions = tokens[0];

        string filePath = rootPath + name;

        if (permissions[0] == 'd')
        {
            ListFtpDirectory(url, filePath + "/", credentials, list);
        }
        else
        {
            list.Add(filePath);
        }
    }
}

使用如下函数:

List<string> list = new List<string>();
NetworkCredential credentials = new NetworkCredential("user", "mypassword");
string url = "ftp://ftp.example.com/";
ListFtpDirectory(url, "", credentials, list);

上述方法的一个缺点是它必须解析特定于服务器的列表来检索有关文件和文件夹的信息。上面的代码需要一个常见的 *nix 样式列表。但许多服务器使用不同的格式。

不幸的是,FtpWebRequest 不支持 MLSD 命令,这是在 FTP 协议中检索具有文件属性的目录列表的唯一可移植方式。


如果您想避免解析服务器特定目录列表格式的麻烦,请使用支持MLSD 命令和/或解析各种LIST 列表格式的第三方库;和递归下载。

例如使用WinSCP .NET assembly,您可以通过一次调用Session.EnumerateRemoteFiles 来列出整个目录:

// Setup session options
SessionOptions sessionOptions = new SessionOptions
{
    Protocol = Protocol.Ftp,
    HostName = "ftp.example.com",
    UserName = "user",
    Password = "mypassword",
};

using (Session session = new Session())
{
    // Connect
    session.Open(sessionOptions);

    // List files
    IEnumerable<string> list =
        session.EnumerateRemoteFiles("/", null, EnumerationOptions.AllDirectories).
        Select(fileInfo => fileInfo.FullName);
}

如果服务器支持,WinSCP 在内部使用MLSD 命令。如果没有,它使用LIST 命令并支持数十种不同的列表格式。

(我是 WinSCP 的作者)

【讨论】:

    【解决方案2】:

    我正在做类似的事情,但不是对每个都使用 StreamReader.ReadLine(),而是使用 StreamReader.ReadToEnd() 一次性完成。您不需要 ReadLine() 来获取目录列表。以下是我的完整代码(完整的操作方法在this 教程中进行了解释):

            FtpWebRequest request=(FtpWebRequest)FtpWebRequest.Create(path);
            request.Method=WebRequestMethods.Ftp.ListDirectoryDetails;
            List<ftpinfo> files=new List<ftpinfo>();
    
            //request.Proxy = System.Net.WebProxy.GetDefaultProxy();
            //request.Proxy.Credentials = CredentialCache.DefaultNetworkCredentials;
            request.Credentials = new NetworkCredential(_username, _password);
            Stream rs=(Stream)request.GetResponse().GetResponseStream();
    
            OnStatusChange("CONNECTED: " + path, 0, 0);
    
            StreamReader sr = new StreamReader(rs);
            string strList = sr.ReadToEnd();
            string[] lines=null;
    
            if (strList.Contains("\r\n"))
            {
                lines=strList.Split(new string[] {"\r\n"},StringSplitOptions.None);
            }
            else if (strList.Contains("\n"))
            {
                lines=strList.Split(new string[] {"\n"},StringSplitOptions.None);
            }
    
            //now decode this string array
    
            if (lines==null || lines.Length == 0)
                return null;
    
            foreach(string line in lines)
            {
                if (line.Length==0)
                    continue;
                //parse line
                Match m= GetMatchingRegex(line);
                if (m==null) {
                    //failed
                    throw new ApplicationException("Unable to parse line: " + line);
                }
    
                ftpinfo item=new ftpinfo();
                item.filename = m.Groups["name"].Value.Trim('\r');
                item.path = path;
                item.size = Convert.ToInt64(m.Groups["size"].Value);
                item.permission = m.Groups["permission"].Value;
                string _dir = m.Groups["dir"].Value;
                if(_dir.Length>0  && _dir != "-")
                {
                    item.fileType = directoryEntryTypes.directory;
                } 
                else
                {
                    item.fileType = directoryEntryTypes.file;
                }
    
                try
                {
                    item.fileDateTime = DateTime.Parse(m.Groups["timestamp"].Value);
                }
                catch
                {
                    item.fileDateTime = DateTime.MinValue; //null;
                }
    
                files.Add(item);
            }
    
            return files;
    

    【讨论】:

    • 非常感谢您的好评。当我读到最后一次时工作。我不必使用整个代码,因为我认为我必须包含该库。再次感谢。
    猜你喜欢
    • 1970-01-01
    • 2016-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-01
    • 2013-06-06
    相关资源
    最近更新 更多