C# 仅当存在匹配文件时才移动文件

C# 仅当存在匹配文件时才移动文件,c#,file-io,sftp,C#,File Io,Sftp,我有一个需要两个文件来处理数据的应用程序。包含实际数据的zip文件,然后是说明如何处理所述数据的控制文件 这些文件通过sftp下载到暂存目录。一旦zip文件完成,我需要检查控制文件是否也在那里。它们只共享一个命名前缀(例如100001_ABCDEF_123456.zip与100001_ABCDEF_control_file.ctl配对) 我试图找到一种方法,等待zip文件完成下载,然后动态移动文件,同时保持目录结构,因为这对于处理的下一步很重要 目前我正在等待sftp工作人员完成,然后打电话给r

我有一个需要两个文件来处理数据的应用程序。包含实际数据的zip文件,然后是说明如何处理所述数据的控制文件

这些文件通过sftp下载到暂存目录。一旦zip文件完成,我需要检查控制文件是否也在那里。它们只共享一个命名前缀(例如100001_ABCDEF_123456.zip与100001_ABCDEF_control_file.ctl配对)

我试图找到一种方法,等待zip文件完成下载,然后动态移动文件,同时保持目录结构,因为这对于处理的下一步很重要

目前我正在等待sftp工作人员完成,然后打电话给robocopy移动所有东西。我希望有一个更完善的方法

我尝试了几种方法,但得到了相同的结果。文件下载但从未移动。由于某些原因,我无法正确进行比较

我曾尝试使用FileSystemWatcher来查找从filepart重命名为zip,但它似乎错过了几次下载,并且由于某种原因,当我到达我的foreach以搜索目录中的控制文件时,该函数将终止。 下面是FileSystemWatcher事件,我为创建和更改调用此事件。 下面是filesystemwatcher的设置

        watcher.Path = @"C:\Sync\";
        watcher.IncludeSubdirectories = true;
        watcher.EnableRaisingEvents = true;
        watcher.Filter = "*.zip";
        watcher.NotifyFilter = NotifyFilters.Attributes |
                               NotifyFilters.CreationTime |
                               NotifyFilters.FileName |
                               NotifyFilters.LastAccess |
                               NotifyFilters.LastWrite |
                               NotifyFilters.Size |
                               NotifyFilters.Security | 
                               NotifyFilters.CreationTime | 
                               NotifyFilters.DirectoryName;
        watcher.Created += Watcher_Changed;
        watcher.Changed += Watcher_Changed;

 private void Watcher_Changed(object sender, FileSystemEventArgs e)
    {
        var dir = new DirectoryInfo(e.FullPath.Substring(0, e.FullPath.Length - e.Name.Length));
        var files = dir.GetFiles();

        FileInfo zipFile = new FileInfo(e.FullPath);

        foreach (FileInfo file in files)
        {
            MessageBox.Show(file.Extension);
            if (file.Extension == "ctl" && file.Name.StartsWith(e.Name.Substring(0, (e.Name.Length - 14))))
            {
                file.CopyTo(@"C:\inp\");
                zipFile.CopyTo(@"C:\inp\");
            }
        }
    }

被改变的观察者将被要求做各种各样的事情,而不是每一次你都想对它做出反应

在事件处理程序中,您应该做的第一件事是尝试以独占方式打开zipFile。如果您做不到,请忽略此事件并等待另一个事件。如果这是FTP服务器,则每次将新数据块写入磁盘时,您都会收到一个更改的事件。您还可以将某些内容置于“重试”状态排队或使用其他机制检查文件是否在以后可用。我在我们的系统中有类似的需求,我们在注意到第一次更改后每5秒尝试一次。只有在我们能够以独占方式打开文件进行写入时,我们才允许它进入下一步

我将加强您对文件名外观的假设。您将搜索限制为*.zip,但不要仅依赖于该目标目录中存在的.zip文件。请验证您正在对文件名进行的解析是否没有遇到意外值。您可能还想检查dir.Exists()调用dir.GetFiles()之前,可能会引发异常


关于丢失的事件,请参阅缓冲区溢出的这个好答案:

众所周知,FileSystemWatcher类的正确使用非常棘手,因为您将为正在写入、移动或复制的单个文件获得多个事件,@WillStoltenberg在他的文章中也提到了这一点

我发现,只设置一个周期性运行的任务(例如每30秒一次)要容易得多。对于您的问题,您可以轻松地执行以下操作。请注意,使用计时器而不是
任务的类似实现。延迟
,可能更可取

public class MyPeriodicWatcher 
{
    private readonly string _watchPath;
    private readonly string _searchMask;
    private readonly Func<string, string> _commonPrefixFetcher;
    private readonly Action<FileInfo, FileInfo> _pairProcessor;
    private readonly TimeSpan _checkInterval;
    private readonly CancellationToken _cancelToken;

    public MyPeriodicWatcher(
        string watchPath,
        string searchMask,
        Func<string, string> commonPrefixFetcher,
        Action<FileInfo, FileInfo> pairProcessor,
        TimeSpan checkInterval,
        CancellationToken cancelToken)
    {
        _watchPath = watchPath;
        _searchMask = string.IsNullOrWhiteSpace(searchMask) ? "*.zip" : searchMask;
        _pairProcessor = pairProcessor;
        _commonPrefixFetcher = commonPrefixFetcher;
        _cancelToken = cancelToken;
        _checkInterval = checkInterval;
    }

    public Task Watch()
    {
        while (!_cancelToken.IsCancellationRequested)
        {
            try
            {
                foreach (var file in Directory.EnumerateFiles(_watchPath, _searchMask))
                {
                    var pairPrefix = _commonPrefixFetcher(file);
                    if (!string.IsNullOrWhiteSpace(pairPrefix))
                    {
                        var match = Directory.EnumerateFiles(_watchPath, pairPrefix + "*.ctl").FirstOrDefault();
                        if (!string.IsNullOrEmpty(match) && !_cancelToken.IsCancellationRequested)
                            _pairProcessor(
                                new FileInfo(Path.Combine(_watchPath, file)),
                                new FileInfo(Path.Combine(_watchPath, match)));
                    }
                    if (_cancelToken.IsCancellationRequested)
                        break;
                }
                if (_cancelToken.IsCancellationRequested)
                    break;

                Task.Delay(_checkInterval, _cancelToken).Wait().ConfigureAwait(false);
            }
            catch (OperationCanceledException)
            {
                break;
            }
        }
    }
}
公共类MyPeriodicWatcher
{
私有只读字符串\u监视路径;
私有只读字符串_searchMask;
专用只读函数\u commonPrefixFetcher;
专用只读操作处理器;
私有只读时间间隔_checkInterval;
私有只读取消令牌\u取消令牌;
公共MyPeriodicWatcher(
字符串监视路径,
字符串搜索掩码,
Func CommonPrefitcher,
动作配对处理器,
时间跨度检查间隔,
取消令牌(取消令牌)
{
_监视路径=监视路径;
_searchMask=string.IsNullOrWhiteSpace(searchMask)?“*.zip”:searchMask;
_成对处理器=成对处理器;
_commonPrefixFetcher=commonPrefixFetcher;
_cancelToken=cancelToken;
_检查间隔=检查间隔;
}
公共任务观察()
{
而(!\u cancelToken.IsCancellationRequested)
{
尝试
{
foreach(目录中的var文件。枚举文件(_watchPath,_searchMask))
{
var pairPrefix=_commonPrefixFetcher(文件);
如果(!string.IsNullOrWhiteSpace(pairPrefix))
{
var match=Directory.EnumerateFiles(_watchPath,pairPrefix+“*.ctl”).FirstOrDefault();
如果(!string.IsNullOrEmpty(match)&&&!\u cancelToken.IsCancellationRequested)
_成对处理器(
新文件信息(Path.Combine(_watchPath,file)),
新文件信息(Path.Combine(_watchPath,match));
}
如果(_cancelToken.IsCancellationRequested)
打破
}
如果(_cancelToken.IsCancellationRequested)
打破
Task.Delay(_checkInterval,_cancelToken).Wait().configurewait(false);
}
捕获(操作取消异常)
{
打破
}
}
}
}
您需要为其提供

  • 监视的路径
  • 第一个文件(即.*.zip)的搜索掩码
  • 从zip文件名获取公共文件名前缀的函数委托
  • 间歇
  • 将执行移动并接收待处理/移动对的
    FileInfo
    的代理
  • 以及用于完全取消监视的取消令牌

在您的
pairProcessor
委托中,捕获IO异常,并检查共享冲突(这可能意味着写入文件尚未完成)。

在您的监视程序中,即监视路径
@“C:\Sync\”
您设置了
watcher.IncludeSubdirectories=true;
。在文件处理中,您将文件移动到
@“C:\Sync\inp\”
。这是观察者正在监视的子目录。Tha