Reputation: 370
My code is
var files = Directory.GetFiles(this.DirectoryPath, this.File.FileMatchPattern, SearchOption.TopDirectoryOnly);
Where File.FileMatchPattern looks something like "FileName*.csv" and DirectoryPath is a unc share.
In dev this code executes fine but in uat we're getting the following exception:
ERROR 8 Guggenheim.Pipeline.Services.FileWatcher.WatchedFile - CheckForFile
System.IO.IOException: The process cannot access the file '\\il1tstbrsapp01\prodapps\FileWatcher\EPAMFileWatcherDropFolderTest\20140531_0171' because it is being used by another process.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.FileSystemEnumerableIterator`1.CommonInit()
at System.IO.FileSystemEnumerableIterator`1..ctor(String path, String originalUserPath, String searchPattern, SearchOption searchOption, SearchResultHandler`1 resultHandler, Boolean checkHost)
at System.IO.Directory.GetFiles(String path, String searchPattern, SearchOption searchOption)
at Guggenheim.Pipeline.Services.FileWatcher.WatchedFile.CheckForFile() in e:\VS\FixedIncome\Pipeline.Services\Guggenheim.Pipeline.Services.FileWatcher\Guggenheim.Pipeline.Services.FileWatcher\WatchedFile.cs:line 71
The weird thing is the path of the error is a directory not a file, it should be noted that that directory is being watched by a FileSystemWatcher.
I don't really care if the file is being accessed by another process, I just want to know if its there at this point. Is there any way to search a directory for files in .NET/C# without caring about locks?
Edit: I'm watching the folder with the following code:
Watcher = new FileSystemWatcher(group.WatchDirectory);
Watcher.IncludeSubdirectories = true;
Watcher.NotifyFilter = NotifyFilters.DirectoryName;
Watcher.Created += Watcher_Created;
WatchedGroups = new List<WatchedGroup>();
Watcher.EnableRaisingEvents = true;
Then on folder created:
private void Watcher_Created(object sender, FileSystemEventArgs e)
{
WatchedGroup group = new WatchedGroup(Group, e.FullPath);
if (group.IsGroupReady)
group_GroupReady(group, new EventArgs());
else
{
group.GroupFailed += group_GroupFailed;
group.GroupReady += group_GroupReady;
WatchedGroups.Add(group);
}
}
WatchedGroup is the following:
public class WatchedGroup : IDisposable
{
public FileWatcherGroup Group { get; private set; }
private Timer GroupTimer { get; set; }
public List<WatchedFile> Files { get; private set; }
public string DirectoryPath { get; private set; }
public event EventHandler GroupReady;
public event EventHandler GroupFailed;
public bool IsGroupReady { get; private set; }
private readonly object sync = new object();
public WatchedGroup(FileWatcherGroup group, string directory)
{
this.Group = group;
this.GroupTimer = new Timer(Group.WaitTimeInMinutes * 60000);
this.GroupTimer.Elapsed += GroupTimer_Elapsed;
this.DirectoryPath = directory;
Files = Group.Files.Select(f => new WatchedFile(f, directory)).ToList();
CheckGroupReady();
if (IsGroupReady)
return;
foreach(var file in Files)
{
file.FileAvailable += File_FileAvailable;
}
this.GroupTimer.Start();
}
private void StopFiles()
{
foreach(var file in Files)
{
file.Stop();
}
this.GroupTimer.Stop();
}
private void CheckFiles()
{
foreach(var file in Files.Where(f => !f.IsFileAvailable))
{
file.CheckForFile();
}
}
private void CheckGroupReady()
{
lock (sync)
{
if (!IsGroupReady && Files.All(f => f.IsFileAvailable))
{
IsGroupReady = true;
StopFiles();
if (GroupReady != null)
GroupReady(this, new EventArgs());
}
}
}
private void File_FileAvailable(object sender, EventArgs e)
{
CheckGroupReady();
}
private void GroupTimer_Elapsed(object sender, ElapsedEventArgs e)
{
GroupTimer.Stop();
StopFiles();
CheckFiles();
CheckGroupReady();
if (!IsGroupReady && GroupFailed != null)
GroupFailed(this, new EventArgs());
}
public void Dispose()
{
this.StopFiles();
this.Files.Clear();
}
}
WatchedFile is the following:
public class WatchedFile
{
private static readonly ILog logger = LogManager.GetLogger(typeof(WatchedFile));
public WatcherFile File { get; private set; }
public bool IsFileAvailable { get; private set; }
public event EventHandler FileAvailable;
private FileSystemWatcher Watcher { get; set; }
public string DirectoryPath { get; private set; }
public string FilePath { get; private set; }
public WatchedFile(WatcherFile file, string directoryPath)
{
this.File = file;
this.DirectoryPath = directoryPath;
CheckForFile();
if (IsFileAvailable)
return;
try
{
Watcher = new FileSystemWatcher(directoryPath);
Watcher.Filter = File.FileMatchPattern;
Watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.LastAccess | NotifyFilters.DirectoryName;
Watcher.Created += Watcher_Created;
Watcher.EnableRaisingEvents = true;
}
catch(Exception ex)
{
logger.Error("WatchedFile FileSystemWatcher", ex);
throw;
}
}
public void Stop()
{
if(Watcher != null)
Watcher.EnableRaisingEvents = false;
}
private void Watcher_Created(object sender, FileSystemEventArgs e)
{
this.FilePath = e.FullPath;
MarkFileAvailable();
}
private readonly object sync = new object();
private void MarkFileAvailable()
{
lock(sync)
{
if(!this.IsFileAvailable)
{
this.IsFileAvailable = true;
if (FileAvailable != null)
FileAvailable(this, new EventArgs());
}
}
}
public bool CheckForFile()
{
try
{
var files = Directory.GetFiles(this.DirectoryPath, this.File.FileMatchPattern, SearchOption.TopDirectoryOnly);
if (files.Length > 0)
{
this.FilePath = files.First();
MarkFileAvailable();
}
return this.IsFileAvailable;
}
catch(Exception ex)
{
logger.Error("CheckForFile", ex);
throw;
}
}
}
The purpose of this code is to allow an end user to drop a folder on a network share which is supposed to contain a set of files, defined by a pattern (FileName*.csv as an example) and if all the files are present within in a set amount of time it triggers an event that downstream systems pickup, if not it triggers a failure event.
Upvotes: 1
Views: 4179
Reputation: 6914
You will get this if you use Directory.GetFiles on a folder that's currently being written to, which is the case here since you say files can be dropped at any time.
See here and here for various thoughts on the discussion.
You may need to consider leaving Directory.GetFiles
out of the equation. Wrapping it in an exception handler with retries would be ugly and not performant.
The second link contains another approach where files are written to an intermediate folder, and only moved to the destination folder (on the same drive) once the write completes.
Upvotes: 3