2016-05-04 87 views
12

一般信息
我还在学习C#的过程。为了帮助自己,我试图创建一个程序,它将自动将我所有的本地项目与FTP服务器上的文件夹同步。这样,无论我在学校还是在家,我总是有相同的项目可供我使用。C#通过FTP下载所有文件和子目录

我知道有像Dropbox这样的程序已经为我做了这件事,但我想到了创造这样的事情,我自己会教会我很多。

问题
我对我的目标第一步是刚刚从我的FTP服务器上下载的所有文件,子目录和子文件。我已经设法从下面的代码下载一个目录中的所有文件。但是,我的代码只列出了主目录中的文件夹名称和文件。子文件夹和子文件不会被返回,也不会被下载。除此之外,服务器返回550错误,因为我试图下载文件夹,就好像它们是文件一样。我已经在这里工作了4个多小时,但我无法找到任何有关如何解决这些问题并使其工作的任何信息。为此,我希望你们能帮助我了:)

代码

public string[] GetFileList() 
{ 
    string[] downloadFiles; 
    StringBuilder result = new StringBuilder(); 
    WebResponse response = null; 
    StreamReader reader = null; 

    try 
    { 
     FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url); 
     request.UseBinary = true; 
     request.Method = WebRequestMethods.Ftp.ListDirectory; 
     request.Credentials = new NetworkCredential(ftpUserName, ftpPassWord); 
     request.KeepAlive = false; 
     request.UsePassive = false; 
     response = request.GetResponse(); 
     reader = new StreamReader(response.GetResponseStream()); 
     string line = reader.ReadLine(); 
     while (line != null) 
     { 
      result.Append(line); 
      result.Append("\n"); 
      line = reader.ReadLine(); 
     } 
     result.Remove(result.ToString().LastIndexOf('\n'), 1); 
     return result.ToString().Split('\n'); 
    } 
    catch (Exception ex) 
    { 
     if (reader != null) 
     { 
      reader.Close(); 
     } 
     if (response != null) 
     { 
      response.Close(); 
     } 
     downloadFiles = null; 
     return downloadFiles; 
    } 
} 

private void Download(string file) 
{ 
    try 
    { 
     string uri = url + "/" + file; 
     Uri serverUri = new Uri(uri); 
     if (serverUri.Scheme != Uri.UriSchemeFtp) 
     { 
      return; 
     } 
     FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url + "/" + file); 
     request.UseBinary = true; 
     request.Method = WebRequestMethods.Ftp.DownloadFile; 
     request.Credentials = new NetworkCredential(ftpUserName, ftpPassWord); 
     request.KeepAlive = false; 
     request.UsePassive = false; 
     FtpWebResponse response = (FtpWebResponse)request.GetResponse(); 
     Stream responseStream = response.GetResponseStream(); 
     FileStream writeStream = new FileStream(localDestnDir + "\\" + file, FileMode.Create);     
     int Length = 2048; 
     Byte[] buffer = new Byte[Length]; 
     int bytesRead = responseStream.Read(buffer, 0, Length); 
     while (bytesRead > 0) 
     { 
      writeStream.Write(buffer, 0, bytesRead); 
      bytesRead = responseStream.Read(buffer, 0, Length); 
     } 
     writeStream.Close(); 
     response.Close(); 
    } 
    catch (WebException wEx) 
    { 
     MessageBox.Show(wEx.Message, "Download Error"); 
    } 
    catch (Exception ex) 
    { 
     MessageBox.Show(ex.Message, "Download Error"); 
    } 
} 

回答

19

FtpWebRequest没有递归文件操作(包括下载)任何明确的支持。你必须执行递归自己:

  • 列出远程目录
  • 迭代项,下载文件,并在递归到子目录(再次列出这些,等)

棘手的部分是确定来自子目录的文件。 FtpWebRequest无法通过便携式方式执行此操作。 FtpWebRequest不幸地不支持MLSD命令,这是在FTP协议中使用文件属性检索目录列表的唯一便携方式。另请参阅Checking if object on FTP server is file or directory

的选项有:

  • 请在是肯定要失败的文件,并成功为目录(反之亦然)的文件名操作。即你可以尝试下载“名称”。如果成功,它就是一个文件,如果失败了,它就是一个目录。
  • 你可能是幸运的,在特定情况下,可以通过文件名告诉从目录中的文件(即所有文件的扩展名,而子目录不)
  • 您使用长的目录列表(LIST命令= ListDirectoryDetails方法)并尝试解析服务器特定的列表。许多FTP服务器使用* nix风格的列表,其中您在入口最开始处用d标识目录。但是许多服务器使用不同的格式。下面的示例使用这种方法(假设* nix的格式)
void DownloadFtpDirectory(string url, NetworkCredential credentials, string localPath) 
{ 
    FtpWebRequest listRequest = (FtpWebRequest)WebRequest.Create(url); 
    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 localFilePath = Path.Combine(localPath, name); 
     string fileUrl = url + name; 

     if (permissions[0] == 'd') 
     { 
      if (!Directory.Exists(localFilePath)) 
      { 
       Directory.CreateDirectory(localFilePath); 
      } 

      DownloadFtpDirectory(fileUrl + "/", credentials, localFilePath); 
     } 
     else 
     { 
      FtpWebRequest downloadRequest = (FtpWebRequest)WebRequest.Create(fileUrl); 
      downloadRequest.Method = WebRequestMethods.Ftp.DownloadFile; 
      downloadRequest.Credentials = credentials; 

      using (FtpWebResponse downloadResponse = 
         (FtpWebResponse)downloadRequest.GetResponse()) 
      using (Stream sourceStream = downloadResponse.GetResponseStream()) 
      using (Stream targetStream = File.Create(localFilePath)) 
      { 
       byte[] buffer = new byte[10240]; 
       int read; 
       while ((read = sourceStream.Read(buffer, 0, buffer.Length)) > 0) 
       { 
        targetStream.Write(buffer, 0, read); 
       } 
      } 
     } 
    } 
} 

使用功能,如:

NetworkCredential credentials = new NetworkCredential("user", "mypassword"); 
string url = "ftp://ftp.example.com/directory/to/download/"; 
DownloadFtpDirectory(url, credentials, @"C:\target\directory"); 

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

例如与WinSCP .NET assembly您可以到Session.GetFiles单个呼叫下载整个目录:

// 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); 

    // Download files 
    session.GetFiles("/directory/to/download/*", @"C:\target\directory\*").Check(); 
} 

内部,WinSCP赋予使用MLSD命令,如果服务器支持。如果不是,则使用LIST命令并支持数十种不同的列表格式。

默认情况下,Session.GetFiles method是递归的。

(我的WinSCP的作者)

+2

我已经改变了你的答案接受的答案,因为我已经学到了很多东西,并为所有的efford你已经把它!再次感谢! – icecub

+0

您好那里我正在尝试使用您的库,但我需要SSL/TLS在我的连接上我通过使用EnableSsl = true的标准FTPrequest执行此操作可以在您的会话选项中设置吗? – Jay

+0

我能够通过将FtpMode和FTPSecure分别设置为Passive和Explicit来获得此连接,伟大的库的递归功能非常出色,一个问题是有一个功能抓取新文件夹中不存在的文件,因此基本上只抓取新文件已经被复制的文件 – Jay

相关问题