监控ASP中的FTP目录。NET/C#

Monitor FTP directory in ASP.NET/C#

我有 FileSystem 个本地目录观察器。它工作正常。我想为 FTP 实现同样的功能。有什么办法可以实现吗?查了很多解决办法都不清楚。

逻辑:想要从 FTP 晚于某个时间戳获取文件。
面临的问题:从 FTP 获取所有文件然后过滤结果会影响性能(使用 FtpWebRequest)。

有什么正确的方法吗? (WinSCP被搁置,现在不能用。)

FileSystemWatcher oFsWatcher = new FileSystemWatcher();
OFSWatchers.Add(oFsWatcher);
oFsWatcher.Path = sFilePath;

oFsWatcher.Filter = string.IsNullOrWhiteSpace(sFileFilter) ? "*.*" : sFileFilter;
oFsWatcher.NotifyFilter = NotifyFilters.FileName;

oFsWatcher.EnableRaisingEvents = true;
oFsWatcher.IncludeSubdirectories = bIncludeSubdirectories;
oFsWatcher.Created += new FileSystemEventHandler(OFsWatcher_Created);

除非您有权访问承载该服务的 OS;会有点难。

FileSystemWatcher 在文件系统上放置一个钩子,它会在发生事情时立即通知您的应用程序。

FTP command specifications没有这样的钩子。除此之外,它始终由客户端发起。

因此,要实现这样的逻辑,您应该定期执行 NLST 以列出 FTP 目录内容并自行跟踪更改(或散列,也许 (MDTM)) .

更多信息:

您不能使用 FileSystemWatcher 或任何其他方式,因为 FTP 协议没有任何 API 来通知客户端远程目录中的更改。

您所能做的就是定期迭代远程树并查找更改。

如果您使用支持远程树的递归列表的 FTP 客户端库,它实际上很容易实现。不幸的是,内置的 .NET FTP 客户端 FtpWebRequest does not. But for example with WinSCP .NET assembly, you can use the Session.EnumerateRemoteFiles method.

查看文章Watching for changes in SFTP/FTP server:

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

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

    List<string> prevFiles = null;

    while (true)
    {
        // Collect file list
        List<string> files =
            session.EnumerateRemoteFiles(
                "/remote/path", "*.*", EnumerationOptions.AllDirectories)
            .Select(fileInfo => fileInfo.FullName)
            .ToList();
        if (prevFiles == null)
        {
            // In the first round, just print number of files found
            Console.WriteLine("Found {0} files", files.Count);
        }
        else
        {
            // Then look for differences against the previous list
            IEnumerable<string> added = files.Except(prevFiles);
            if (added.Any())
            {
                Console.WriteLine("Added files:");
                foreach (string path in added)
                {
                    Console.WriteLine(path);
                }
            }

            IEnumerable<string> removed = prevFiles.Except(files);
            if (removed.Any())
            {
                Console.WriteLine("Removed files:");
                foreach (string path in removed)
                {
                    Console.WriteLine(path);
                }
            }
        }

        prevFiles = files;

        Console.WriteLine("Sleeping 10s...");
        Thread.Sleep(10000);
    }
}

(我是WinSCP的作者)


不过,如果您真的只想下载更改,这会更简单。只需在循环中使用 Session.SynchronizeDirectories

while (true)
{
    SynchronizationResult result =
        session.SynchronizeDirectories(
            SynchronizationMode.Local, "/remote/path", @"C:\local\path", true);
    result.Check();
    // You can inspect result.Downloads for a list for updated files

    Console.WriteLine("Sleeping 10s...");
    Thread.Sleep(10000);
}

这甚至会更新修改过的文件,而不仅仅是新文件。


尽管从 Web 应用程序使用 WinSCP .NET 程序集可能会出现问题。如果您不想使用第三方库,则必须处理 FtpWebRequest 的限制。有关如何使用 FtpWebRequest 递归列出远程目录树的示例,请参阅我对 .

的回答

您已编辑您的问题,表明您在使用我建议的解决方案时遇到了性能问题。尽管您已经提出了一个涵盖此内容的新问题:

我有一个替代解决方案来实现我的功能。

解释:

我正在从 FTP(需要读取权限)下载具有相同文件夹结构的文件。

所以每次 job/service 运行时我都可以检查物理路径是否存在相同的文件(完整路径)如果不存在则可以将其视为新文件。 Ii 也可以做一些相同的操作并下载。

它只是一个替代解决方案。

代码更改:

 private static void GetFiles()
 {
    using (FtpClient conn = new FtpClient())
    {
        string ftpPath = "ftp://myftp/";

        string downloadFileName = @"C:\temp\FTPTest\";

        downloadFileName +=  "\";

        conn.Host = ftpPath;
        //conn.Credentials = new NetworkCredential("ftptest", "ftptest");
        conn.Connect();

        //Get all directories

        foreach (FtpListItem item in conn.GetListing(conn.GetWorkingDirectory(),
            FtpListOption.Modify | FtpListOption.Recursive))
        {
            // if this is a file
            if (item.Type == FtpFileSystemObjectType.File)
            {
                string localFilePath = downloadFileName + item.FullName;

                //Only newly created files will be downloaded.
                if (!File.Exists(localFilePath))
                {
                    conn.DownloadFile(localFilePath, item.FullName);
                    //Do any action here.
                    Console.WriteLine(item.FullName);
                }
            }
        }
    }
}