用于 FTP 的文件系统观察器

发布于 2024-08-02 02:17:13 字数 252 浏览 6 评论 0原文

如何为 FTP 位置实现 FileSystemWatcher(在 C# 中)。 这个想法是,每当 FTP 位置添加任何内容时,我都希望将其复制到我的本地计算机。 任何想法都会有所帮助。

这是我之前的问题使用 .NET 进行选择性 FTP 下载的后续问题。

How can I implement a FileSystemWatcher for an FTP location (in C#). The idea is whenever anything gets added in the FTP location I wish to copy it to my local machine. Any ideas will be helpful.

This is a follow up of my previous question Selective FTP download using .NET.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

做个少女永远怀春 2024-08-09 02:17:13

您将必须实施轮询解决方案,定期询问目录内容。 将其与上次调用的缓存列表进行比较,并确定发生了什么情况。

不幸的是,FTP 协议中没有任何内容可以帮助您解决此问题。

You're going to have to implement a polling solution, where you keep asking for the directory content periodically. Compare this to a cached list from the previous call and determine what happened that way.

There's nothing in the FTP protocol that will help you with this unfortunately.

鹿港巷口少年归 2024-08-09 02:17:13

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

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

如果您使用支持远程树递归列表的 FTP 客户端库,那么实际上很容易实现。 不幸的是,内置的 .NET FTP 客户端 FtpWebRequest 没有。 但例如使用 WinSCP .NET 程序集,您可以使用 Session.EnumerateRemoteFiles 方法

请参阅文章监视 SFTP/FTP 服务器中的更改

// 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 即可。

session.SynchronizeDirectories(
    SynchronizationMode.Local, "/remote/path", @"C:\local\path", true).Check();

请参阅文章使本地目录保持最新(从远程 SFTP/FTP 服务器下载更改的文件)


如果您不想使用第三方库,则必须考虑 FtpWebRequest 的限制。 有关如何使用 FtpWebRequest 递归列出远程目录树的示例,请参阅我对 C# 下载所有文件的回答和子目录通过 FTP

You cannot use the FileSystemWatcher or any other way, because the FTP protocol does not have any API to notify a client about changes in the remote directory.

All you can do is to periodically iterate the remote tree and find changes.

It's actually rather easy to implement, if you use an FTP client library that supports recursive listing of a remote tree. Unfortunately, the built-in .NET FTP client, the FtpWebRequest does not. But for example with WinSCP .NET assembly, you can use the Session.EnumerateRemoteFiles method.

See the article 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);
    }
}

(I'm the author of WinSCP)


Though, if you actually want to just download the changes, it's a way easier. Just use the Session.SynchronizeDirectories in the loop.

session.SynchronizeDirectories(
    SynchronizationMode.Local, "/remote/path", @"C:\local\path", true).Check();

See the article Keep local directory up to date (download changed files from remote SFTP/FTP server).


If you do not want to use a 3rd party library, you have to do with limitations of the FtpWebRequest. For an example how to recursively list a remote directory tree with the FtpWebRequest, see my answer to C# Download all files and subdirectories through FTP.

£冰雨忧蓝° 2024-08-09 02:17:13

FileSystemWatcher 类通过向主机 Windows 操作系统注册事件来工作。 因此,它仅限于处理本地路径和 Windows 系统上托管的目录的 UNC 路径。 FileSystemWatcher 上的 MSDN 文档解释了可以使用的路径以及使用该类的一些潜在问题。

如果您希望收到有关 FTP 站点上的更改的警报,则必须使用轮询机制来询问您感兴趣监视的文件或文件夹的当前状态。 通过比较 FTP 站点的快照以了解更改并在检测到更改时引发类似事件,您将能够了解何时添加和删除文件。 不幸的是,您将无法检测重命名事件,但通过这种方式监视其他更改应该很简单。

The FileSystemWatcher class works by registering for events with the host Windows operating system. As such, it is limited to working on local paths and UNC paths to directories hosted on Windows systems. The MSDN documentation on FileSystemWatcher explains the paths which you can use and some of the potential problems with using the class.

If you are looking to be alerted to changes on an FTP site, you will have to use a polling mechanism to ask for the current status of files or folders you are interested in monitoring. You will be able to see when files are added and removed by comparing snapshots of the FTP site for changes and raising similar events when you detect changes. Unfortunately you wont be able to detect rename events, but other changes should be simple to monitor this way.

执手闯天涯 2024-08-09 02:17:13

您可以通过以下方法监控FTP位置:

public class FtpFileSystemWatcher
{

    public bool IsRunning
    {
        get;
        private set;
    }
    public string FtpUserName
    {
        get;
        set;
    }
    public string FtpPassword
    {
        get;
        set;
    }
    public string FtpLocationToWatch
    {
        get;
        set;
    }
    public string DownloadTo
    {
        get;
        set;
    }
    public bool KeepOrignal
    {
        get;
        set;
    }
    public bool OverwriteExisting
    {
        get;
        set;
    }
    public int RecheckIntervalInSeconds
    {
        get;
        set;
    }
    private bool DownloadInprogress
    {
        get;
        set;
    }

    private System.Timers.Timer JobProcessor;

    public FtpFileSystemWatcher(string FtpLocationToWatch = "", string DownloadTo = "", int RecheckIntervalInSeconds = 1, string UserName = "", string Password = "", bool KeepOrignal = false, bool OverwriteExisting = false)
    {
        this.FtpUserName = UserName;
        this.FtpPassword = Password;
        this.FtpLocationToWatch = FtpLocationToWatch;
        this.DownloadTo = DownloadTo;
        this.KeepOrignal = KeepOrignal;
        this.RecheckIntervalInSeconds = RecheckIntervalInSeconds;
        this.OverwriteExisting = OverwriteExisting;

        if (this.RecheckIntervalInSeconds < 1) this.RecheckIntervalInSeconds = 1;
    }

    public void StartDownloading()
    {

        JobProcessor = new Timer(this.RecheckIntervalInSeconds * 1000);
        JobProcessor.AutoReset = false;
        JobProcessor.Enabled = false;
        JobProcessor.Elapsed += (sender, e) =>
        {
            try
            {

                this.IsRunning = true;

                string[] FilesList = GetFilesList(this.FtpLocationToWatch, this.FtpUserName, this.FtpPassword);

                if (FilesList == null || FilesList.Length < 1)
                {
                    return;
                }

                foreach (string FileName in FilesList)
                {
                    if (!string.IsNullOrWhiteSpace(FileName))
                    {
                        DownloadFile(this.FtpLocationToWatch, this.DownloadTo, FileName.Trim(), this.FtpUserName, this.FtpPassword, this.OverwriteExisting);

                        if (!this.KeepOrignal)
                        {
                            DeleteFile(Path.Combine(this.FtpLocationToWatch, FileName.Trim()), this.FtpUserName, this.FtpPassword);
                        }
                    }
                }

                this.IsRunning = false;
                JobProcessor.Enabled = true;                    
            }

            catch (Exception exp)
            {
                this.IsRunning = false;
                JobProcessor.Enabled = true;
                Console.WriteLine(exp.Message);
            }
        };

        JobProcessor.Start();
    }

    public void StopDownloading()
    {
        try
        {
            this.JobProcessor.Dispose();
            this.IsRunning = false;
        }
        catch { }
    }

    private void DeleteFile(string FtpFilePath, string UserName, string Password)
    {
        FtpWebRequest FtpRequest;
        FtpRequest = (FtpWebRequest)FtpWebRequest.Create(new Uri(FtpFilePath));
        FtpRequest.UseBinary = true;
        FtpRequest.Method = WebRequestMethods.Ftp.DeleteFile;

        FtpRequest.Credentials = new NetworkCredential(UserName, Password);
        FtpWebResponse response = (FtpWebResponse)FtpRequest.GetResponse();
        response.Close();

    }
    private void DownloadFile(string FtpLocation, string FileSystemLocation, string FileName, string UserName, string Password, bool OverwriteExisting)
    {
        try
        {
            const int BufferSize = 2048;
            byte[] Buffer = new byte[BufferSize];

            FtpWebRequest Request;
            FtpWebResponse Response;

            if (File.Exists(Path.Combine(FileSystemLocation, FileName)))
            {
                if (OverwriteExisting)
                {
                    File.Delete(Path.Combine(FileSystemLocation, FileName));
                }
                else
                {
                    Console.WriteLine(string.Format("File {0} already exist.", FileName));
                    return;
                }
            }

            Request = (FtpWebRequest)FtpWebRequest.Create(new Uri(Path.Combine(FtpLocation, FileName)));
            Request.Credentials = new NetworkCredential(UserName, Password);
            Request.Proxy = null;
            Request.Method = WebRequestMethods.Ftp.DownloadFile;
            Request.UseBinary = true;

            Response = (FtpWebResponse)Request.GetResponse();

            using (Stream s = Response.GetResponseStream())
            {
                using (FileStream fs = new FileStream(Path.Combine(FileSystemLocation, FileName), FileMode.CreateNew, FileAccess.ReadWrite))
                {
                    while (s.Read(Buffer, 0, BufferSize) != -1)
                    {
                        fs.Write(Buffer, 0, BufferSize);
                    }
                }
            }
        }
        catch { }

    }
    private string[] GetFilesList(string FtpFolderPath, string UserName, string Password)
    {
        try
        {
            FtpWebRequest Request;
            FtpWebResponse Response;

            Request = (FtpWebRequest)FtpWebRequest.Create(new Uri(FtpFolderPath));
            Request.Credentials = new NetworkCredential(UserName, Password);
            Request.Proxy = null;
            Request.Method = WebRequestMethods.Ftp.ListDirectory;
            Request.UseBinary = true;

            Response = (FtpWebResponse)Request.GetResponse();
            StreamReader reader = new StreamReader(Response.GetResponseStream());
            string Data = reader.ReadToEnd();

            return Data.Split('\n');
        }
        catch
        {
            return null;
        }
    }


}

You can monitor the FTP location by following method:

public class FtpFileSystemWatcher
{

    public bool IsRunning
    {
        get;
        private set;
    }
    public string FtpUserName
    {
        get;
        set;
    }
    public string FtpPassword
    {
        get;
        set;
    }
    public string FtpLocationToWatch
    {
        get;
        set;
    }
    public string DownloadTo
    {
        get;
        set;
    }
    public bool KeepOrignal
    {
        get;
        set;
    }
    public bool OverwriteExisting
    {
        get;
        set;
    }
    public int RecheckIntervalInSeconds
    {
        get;
        set;
    }
    private bool DownloadInprogress
    {
        get;
        set;
    }

    private System.Timers.Timer JobProcessor;

    public FtpFileSystemWatcher(string FtpLocationToWatch = "", string DownloadTo = "", int RecheckIntervalInSeconds = 1, string UserName = "", string Password = "", bool KeepOrignal = false, bool OverwriteExisting = false)
    {
        this.FtpUserName = UserName;
        this.FtpPassword = Password;
        this.FtpLocationToWatch = FtpLocationToWatch;
        this.DownloadTo = DownloadTo;
        this.KeepOrignal = KeepOrignal;
        this.RecheckIntervalInSeconds = RecheckIntervalInSeconds;
        this.OverwriteExisting = OverwriteExisting;

        if (this.RecheckIntervalInSeconds < 1) this.RecheckIntervalInSeconds = 1;
    }

    public void StartDownloading()
    {

        JobProcessor = new Timer(this.RecheckIntervalInSeconds * 1000);
        JobProcessor.AutoReset = false;
        JobProcessor.Enabled = false;
        JobProcessor.Elapsed += (sender, e) =>
        {
            try
            {

                this.IsRunning = true;

                string[] FilesList = GetFilesList(this.FtpLocationToWatch, this.FtpUserName, this.FtpPassword);

                if (FilesList == null || FilesList.Length < 1)
                {
                    return;
                }

                foreach (string FileName in FilesList)
                {
                    if (!string.IsNullOrWhiteSpace(FileName))
                    {
                        DownloadFile(this.FtpLocationToWatch, this.DownloadTo, FileName.Trim(), this.FtpUserName, this.FtpPassword, this.OverwriteExisting);

                        if (!this.KeepOrignal)
                        {
                            DeleteFile(Path.Combine(this.FtpLocationToWatch, FileName.Trim()), this.FtpUserName, this.FtpPassword);
                        }
                    }
                }

                this.IsRunning = false;
                JobProcessor.Enabled = true;                    
            }

            catch (Exception exp)
            {
                this.IsRunning = false;
                JobProcessor.Enabled = true;
                Console.WriteLine(exp.Message);
            }
        };

        JobProcessor.Start();
    }

    public void StopDownloading()
    {
        try
        {
            this.JobProcessor.Dispose();
            this.IsRunning = false;
        }
        catch { }
    }

    private void DeleteFile(string FtpFilePath, string UserName, string Password)
    {
        FtpWebRequest FtpRequest;
        FtpRequest = (FtpWebRequest)FtpWebRequest.Create(new Uri(FtpFilePath));
        FtpRequest.UseBinary = true;
        FtpRequest.Method = WebRequestMethods.Ftp.DeleteFile;

        FtpRequest.Credentials = new NetworkCredential(UserName, Password);
        FtpWebResponse response = (FtpWebResponse)FtpRequest.GetResponse();
        response.Close();

    }
    private void DownloadFile(string FtpLocation, string FileSystemLocation, string FileName, string UserName, string Password, bool OverwriteExisting)
    {
        try
        {
            const int BufferSize = 2048;
            byte[] Buffer = new byte[BufferSize];

            FtpWebRequest Request;
            FtpWebResponse Response;

            if (File.Exists(Path.Combine(FileSystemLocation, FileName)))
            {
                if (OverwriteExisting)
                {
                    File.Delete(Path.Combine(FileSystemLocation, FileName));
                }
                else
                {
                    Console.WriteLine(string.Format("File {0} already exist.", FileName));
                    return;
                }
            }

            Request = (FtpWebRequest)FtpWebRequest.Create(new Uri(Path.Combine(FtpLocation, FileName)));
            Request.Credentials = new NetworkCredential(UserName, Password);
            Request.Proxy = null;
            Request.Method = WebRequestMethods.Ftp.DownloadFile;
            Request.UseBinary = true;

            Response = (FtpWebResponse)Request.GetResponse();

            using (Stream s = Response.GetResponseStream())
            {
                using (FileStream fs = new FileStream(Path.Combine(FileSystemLocation, FileName), FileMode.CreateNew, FileAccess.ReadWrite))
                {
                    while (s.Read(Buffer, 0, BufferSize) != -1)
                    {
                        fs.Write(Buffer, 0, BufferSize);
                    }
                }
            }
        }
        catch { }

    }
    private string[] GetFilesList(string FtpFolderPath, string UserName, string Password)
    {
        try
        {
            FtpWebRequest Request;
            FtpWebResponse Response;

            Request = (FtpWebRequest)FtpWebRequest.Create(new Uri(FtpFolderPath));
            Request.Credentials = new NetworkCredential(UserName, Password);
            Request.Proxy = null;
            Request.Method = WebRequestMethods.Ftp.ListDirectory;
            Request.UseBinary = true;

            Response = (FtpWebResponse)Request.GetResponse();
            StreamReader reader = new StreamReader(Response.GetResponseStream());
            string Data = reader.ReadToEnd();

            return Data.Split('\n');
        }
        catch
        {
            return null;
        }
    }


}
七色彩虹 2024-08-09 02:17:13

编写一个简单的服务来创建 FileSystemWatcher,指向您的 ftp 位置。

然后,当上传或修改文件时,将在您的服务中触发一个事件,然后您可以使用该事件将文件复制到本地计算机。
File.Copy 等。

看看:此博客

Write a simple service to create FileSystemWatcher, pointing at your ftp location.

Then when a file is uploaded or modified, an event will be fired in your service, which you can then use to copy the file to your local machine.
File.Copy etc.

Hav a look at: this blog

短暂陪伴 2024-08-09 02:17:13

我处理这个问题的方法是上传一个单元素字节数组,名为“.ftpComplete”。 FileSystemWatcher 仅监视“.ftpComplete”文件,并将其从末尾剥离以了解实际上传的文件。 由于“.ftpComplete”文件只有 1 个字节,它的上传速度与在 FTP 服务器上创建时的速度差不多,因此一旦您对主上传文件执行了任何需要的操作,就可以将其删除

        FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create(
            FTPAddress + "/" + Path.GetFileName(filePath) + ".ftpComplete");
        request.Method = WebRequestMethods.Ftp.UploadFile;
        request.Credentials = new NetworkCredential(username, password);
        request.UsePassive = true;
        request.UseBinary = true;
        request.KeepAlive = false;
        byte[] buffer = new byte[1];
        Stream reqStream = request.GetRequestStream();
        reqStream.Write(buffer, 0, buffer.Length);
        reqStream.Close();

The way I handle this is to upload a one element byte array, named ".ftpComplete". The FileSystemWatcher only watches for ".ftpComplete" files, and strips that off the end to know the actual file uploaded. Since the".ftpComplete" file is only 1 byte, it uploads about as fast as it is created on the FTP server, so it can be deleted once you do whatever you need to with the main uploaded file

        FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create(
            FTPAddress + "/" + Path.GetFileName(filePath) + ".ftpComplete");
        request.Method = WebRequestMethods.Ftp.UploadFile;
        request.Credentials = new NetworkCredential(username, password);
        request.UsePassive = true;
        request.UseBinary = true;
        request.KeepAlive = false;
        byte[] buffer = new byte[1];
        Stream reqStream = request.GetRequestStream();
        reqStream.Write(buffer, 0, buffer.Length);
        reqStream.Close();
请帮我爱他 2024-08-09 02:17:13

您可以使用 Robo-FTP 脚本来监视 FTP 站点的更改。 以下是示例脚本的链接,该脚本会在检测到更改时发送电子邮件: http:// /kb.robo-ftp.com/script_library/show/40

我查看了您链接的上一个问题。 我认为您应该能够修改 Robo-FTP 示例并使用 SETLEFT 命令加上 /split 选项,使其解析已更改文件的文件夹名称和 ISO 文件编号,然后将文件移动到正确的位置。

You could use a Robo-FTP script to monitor the FTP site for changes. Here is a link to a sample script that sends an email whenever a change is detected: http://kb.robo-ftp.com/script_library/show/40

I looked at the previous question that you linked. I think you should be able to modify the Robo-FTP sample and use the SETLEFT command with the /split option to make it parse the folder name and ISO file number of the changed file and then move the file to the proper location.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文