【问题标题】:Download new and modified files from an FTP server从 FTP 服务器下载新的和修改过的文件
【发布时间】:2012-04-14 08:50:04
【问题描述】:

我正在尝试获取 FTP 服务器上的文件列表,然后一一检查该文件是否存在于本地系统上,是否比较修改的日期以及 ftp 文件是否较新下载它。

private void btnGo_Click(object sender, EventArgs e)
{
    string[] files = GetFileList();
    foreach (string file in files)
    {
        if (file.Length >= 5)
        {
            string uri = "ftp://" + ftpServerIP + "/" + remoteDirectory + "/" + file;
            Uri serverUri = new Uri(uri);

            CheckFile(file);
        }
    }
    this.Close();
}

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

    try
    {
        FtpWebRequest reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri("ftp://" + ftpServerIP + "/" + remoteDirectory));
        reqFTP.UseBinary = true;
        reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
        reqFTP.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
        reqFTP.Proxy = null;
        reqFTP.KeepAlive = false;
        reqFTP.UsePassive = false;
        response = reqFTP.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
    {
        if (reader != null)
        {
            reader.Close();
        }
        if (response != null)
        {
            response.Close();
        }
        downloadFiles = null;
        return downloadFiles;
    }
}

private void CheckFile(string file)
{
    string dFile = file;
    string[] splitDownloadFile = Regex.Split(dFile, " ");
    string fSize = splitDownloadFile[13];
    string fMonth = splitDownloadFile[14];
    string fDate = splitDownloadFile[15];
    string fTime = splitDownloadFile[16];
    string fName = splitDownloadFile[17];


    string dateModified = fDate + "/" + fMonth+ "/" + fYear;

    DateTime lastModifiedDF = Convert.ToDateTime(dateModified);

    string[] filePaths = Directory.GetFiles(localDirectory);

    // if there is a file in filePaths that is the same as on the server compare them and then download if file on server is newer
    foreach (string ff in filePaths)
    {

        string[] splitFile = Regex.Split(ff, @"\\");

        string fileName = splitFile[2];
        FileInfo fouFile = new FileInfo(ff);
        DateTime lastChangedFF = fouFile.LastAccessTime;
        if (lastModifiedDF > lastChangedFF) Download(fileName);
    }
}

在检查文件方法中,对于每个文件(它们是 .exe 文件),当我拆分字符串时,我不断得到不同的结果,即一个文件的文件名在第 18 列,另一个在第 16 列,等等。我也可以' 并不总是得到文件的年份部分。

【问题讨论】:

    标签: c# .net ftp download ftpwebrequest


    【解决方案1】:

    为此,您需要检索远程目录列表,包括时间戳。

    不幸的是,没有真正可靠和有效的方法来使用 .NET 框架提供的功能来检索时间戳,因为它不支持 FTP MLSD 命令。 MLSD 命令以标准化的机器可读格式提供远程目录列表。命令和格式由RFC 3659标准化。

    .NET 框架支持的您可以使用的替代方案(如其他答案所示):

    • ListDirectoryDetails method(FTPLIST命令)检索目录中所有文件的详细信息然后你处理FTP服务器特定格式的详细信息(*nix格式类似于ls*nix命令是最常见的缺点是格式可能会随着时间而改变,因为较新的文件使用“May 8 17:48”格式,而旧文件使用“Oct 18 2009”格式)

      DOS/Windows 格式:C# class to parse WebRequestMethods.Ftp.ListDirectoryDetails FTP response
      *nix 格式:Parsing FtpWebRequest ListDirectoryDetails line

    • GetDateTimestamp method(一个 FTP MDTM 命令)单独检索每个文件的时间戳。一个优点是响应由RFC 3659YYYYMMDDHHMMSS[.sss] 标准化。缺点是您必须为每个文件发送单独的请求,这可能效率很低。

      const string uri = "ftp://ftp.example.com/remote/path/file.txt";
      FtpWebRequest request = (FtpWebRequest)WebRequest.Create(uri);
      request.Method = WebRequestMethods.Ftp.GetDateTimestamp;
      FtpWebResponse response = (FtpWebResponse)request.GetResponse();
      Console.WriteLine("{0} {1}", uri, response.LastModified);
      

    或者,您可以使用支持现代MLSD 命令的第 3 方 FTP 客户端实现。

    例如WinSCP .NET assembly 支持。

    您可以使用Session.ListDirectorySession.EnumerateRemoteFiles 方法读取返回集合中文件的RemoteFileInfo.LastWriteTime

    或者更简单,您可以使用Session.SynchronizeDirectories 让库自动下载(同步)修改后的文件:

    // 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);
    
        // Synchronize files
        session.SynchronizeDirectories(
            SynchronizationMode.Local, @"d:\www", "/remote/path", false).Check();
    }
    

    (我是 WinSCP 的作者)

    【讨论】:

      【解决方案2】:

      首先,您可以在此处找到一些可以从 ftp 获取信息和下载数据的组件:http://www.limilabs.com/ftp

      我写了一些从 ftp 获取文件名和最后修改日期的方法。

      这就是我从行中获取文件名的方式:

      private string GetFtpName(string line)
      {
          for (int i = 0; i < 8; i++)
              line = line.Substring(line.IndexOf(" ")).Trim();
          return line;
      }
      

      这就是我从 ftp 获取上次修改日期的方式:

      private DateTime GetFtpFileDate(string url, ICredentials credential)
      {
          FtpWebRequest rd = (FtpWebRequest)WebRequest.Create(url);
          rd.Method = WebRequestMethods.Ftp.GetDateTimestamp;
          rd.Credentials = credential;
      
          FtpWebResponse response = (FtpWebResponse)rd.GetResponse();
          DateTime lmd = response.LastModified;
          response.Close();
      
          return lmd;
      }
      

      【讨论】:

        【解决方案3】:

        试试

        ListDirectory + GetDateTimestamp 
        

        而不是

        ListDirectoryDetails
        

        【讨论】:

        • 只获取文件名。我需要能够看到文件的修改日期
        • @Wellie:对ListDirectory 返回的每个文件文件使用GetDateTimestamp。这将使您摆脱字符串状态表示解析问题
        【解决方案4】:

        选项 A:我建议您使用更高级别的 FTP 客户端库来为您处理其中一些细节,一些可能的选项是:

        选项 B:为了更直接地回答您的问题,我认为问题在于这一行:

        string[] splitDownloadFile = Regex.Split(dFile, " ");
        

        似乎 FTP 服务器正在使用空格来右对齐文件名。为了解决这个问题,我们想调整正则表达式以消耗字段之间的所有空格:

        string[] splitDownloadFile = Regex.Split(dFile, "\s+");
        

        ...其中 \s 代表任何空白字符(通常是制表符或空格),而 + 表示其左侧的一个或多个事物。这不会处理边缘情况,例如其中包含空格的文件名。

        【讨论】:

        • 这是个坏主意。我尝试了 ftplib 和 ftps,两者都不稳定,因为它们会间歇性地抛出异常。即使我将我的呼叫置于重试循环中多达 5 次,我仍然无法让它们可靠地工作。 ftplib 抛出了无法在主线程上捕获的异常,使我的 Windows 服务每隔一小时左右就崩溃一次。我不能代表 netftp,因为我没有尝试过。我最终使用了标准的 .NET FtpWebRequest 类和非标准的 IIS 配置(通过端口 21 的 ftps)来使其正常工作。我确实最终使用了 ftplib 中的解析器来获取目录列表详细信息。
        【解决方案5】:

        这是来自FTPclient 来源的摘录,向您展示了他们是如何构建自己的。 FtpFileInfo 对象。我无法对此进行测试,以确保目前它适用于所有情况,但也许它会给你一些想法。

            /// <summary>
            /// Return a detailed directory listing, and also download datetime stamps if specified
            /// </summary>
            /// <param name="directory">Directory to list, e.g. /pub/etc</param>
            /// <param name="doDateTimeStamp">Boolean: set to True to download the datetime stamp for files</param>
            /// <returns>An FTPDirectory object</returns>
            public FTPdirectory ListDirectoryDetail(string directory, bool doDateTimeStamp)
            {
                System.Net.FtpWebRequest ftp = GetRequest(GetDirectory(directory));
                // Set request to do simple list
                ftp.Method = System.Net.WebRequestMethods.Ftp.ListDirectoryDetails;
        
                string str = GetStringResponse(ftp);
                // replace CRLF to CR, remove last instance
                str = str.Replace("\r\n", "\r").TrimEnd('\r');
                // split the string into a list
                FTPdirectory dir = new FTPdirectory(str, _lastDirectory);
        
                // download timestamps if requested
                if (doDateTimeStamp)
                {
                    foreach (FTPfileInfo fi in dir)
                    {
                        fi.FileDateTime = this.GetDateTimestamp(fi);
                    }
                }
        
                return dir;
            }
        
            /// <summary>
            /// Obtain datetimestamp for remote file
            /// </summary>
            /// <param name="filename"></param>
            /// <returns></returns>
            public DateTime GetDateTimestamp(string filename)
            {
                string path;
                if (filename.Contains("/"))
                {
                    path = AdjustDir(filename);
                }
                else
                {
                    path = this.CurrentDirectory + filename;
                }
                string URI = this.Hostname + path;
                FtpWebRequest ftp = GetRequest(URI);
                ftp.Method = WebRequestMethods.Ftp.GetDateTimestamp;
                return this.GetLastModified(ftp);
            }
        

        【讨论】:

          猜你喜欢
          • 2015-07-27
          • 2014-08-25
          • 1970-01-01
          • 1970-01-01
          • 2011-10-26
          • 2012-07-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多