我有一个需要两个文件来处理数据的应用程序。一个包含实际数据的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\");
}
}
}
答案 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;
}
}
}
}
您需要提供
FileInfo
。在pairProcessor
委托中,捕获IO异常,并检查共享违规(这可能意味着写入文件尚未完成)。