仅在匹配文件存在时才移动文件

时间:2015-04-28 19:42:13

标签: c# file-io sftp

我有一个需要两个文件来处理数据的应用程序。一个包含实际数据的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\");
            }
        }
    }

2 个答案:

答案 0 :(得分:1)

Watcher_Changed会被各种各样的东西调用,而不是每次调用它都会对它做出反应。

您应该在事件处理程序中首先尝试独占打开zipFile。如果您不能这样做,请忽略此事件并等待另一个事件。如果这是一个FTP服务器,每次将新的数据块写入磁盘时,您将获得更改的事件。您还可以在“重试”队列中放置一些内容,或者使用其他一些机制来检查文件是否可用。我的系统有类似的需求,我们在发现第一次更改后每5秒钟尝试一次。只有当我们可以专门打开文件进行写作时,我们是否允许它继续下一步。

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

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

答案 1 :(得分:0)

正确使用FileSystemWatcher类是非常棘手的,因为正如@WillStoltenberg在answer中提到的那样,您将为正在写入,移动或复制的单个文件获取多个事件。

我发现设置定期运行的任务(例如每30秒)要容易得多。对于您的问题,您可以轻松地执行以下操作。请注意,使用Timer而不是Task.Delay的类似实现可能更可取。

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;
            }
        }
    }
}

您需要提供

  • 监控路径
  • 第一个文件的搜索掩码(即* .zip)
  • 从zip文件名
  • 获取公共文件名前缀的函数委托
  • 间隔
  • 将执行移动的代理人,并接收要处理/移动的对的FileInfo
  • 和取消令牌以彻底取消监控。

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