Skip to content

Instantly share code, notes, and snippets.

@stevebeauge
Created July 4, 2018 13:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stevebeauge/088603022062a683929abb6784451e4d to your computer and use it in GitHub Desktop.
Save stevebeauge/088603022062a683929abb6784451e4d to your computer and use it in GitHub Desktop.
Reliable replacement for FileSystemWatcher
using System;
using System.Threading;
using System.Threading.Tasks;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
public class FileSystemPoller
{
private readonly string _path;
private readonly CancellationToken _cancellationToken;
private readonly string _searchPattern;
private readonly SearchOption _searchOptions;
private readonly HashSet<string> _filesProcessed = new HashSet<string>();
private readonly TimeSpan _interval;
public FileSystemPoller(
string path,
CancellationToken cancellationToken,
string searchPattern = null,
SearchOption options = SearchOption.TopDirectoryOnly,
TimeSpan? interval = null
)
{
if (path == null) throw new ArgumentNullException(nameof(path));
_path = path;
_cancellationToken = cancellationToken;
_searchPattern = searchPattern ?? "*";
_searchOptions = options;
_interval = interval.GetValueOrDefault(TimeSpan.FromSeconds(1));
}
private void PollForChanges(BlockingCollection<string> outQueue)
{
try
{
var newfiles = Directory.GetFiles(_path, _searchPattern, _searchOptions);
foreach (var file in newfiles)
{
if (!_filesProcessed.Contains(file) && !IsFileLocked(file))
{
Debug.WriteLine($"File created disk : {file}");
outQueue.Add(file);
_filesProcessed.Add(file);
}
}
// Remove from history files no more presents
var countBefore = _filesProcessed.Count;
_filesProcessed.RemoveWhere(fileName => !File.Exists(fileName));
Debug.WriteLine($"Files no more presents : {_filesProcessed.Count - countBefore}");
Debug.WriteLine("Poll directory changes");
}
catch (Exception exc)
{
// Actually, there is here a call to the global logging system
Debug.WriteLine(exc.ToString());
}
}
public IEnumerable<string> GetCreatedFiles()
{
if (_cancellationToken.IsCancellationRequested) yield break;
using (var queue = new BlockingCollection<string>())
{
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
TaskUtilities.Run(() => PollForChanges(queue), _interval, _cancellationToken);
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
if (!_cancellationToken.IsCancellationRequested)
{
foreach (var fileInfo in queue.GetConsumingEnumerable(_cancellationToken))
{
yield return fileInfo;
}
}
}
}
private static bool IsFileLocked(string filePath)
{
try
{
using (var stream = File.Open(filePath, FileMode.Open,
FileAccess.ReadWrite, FileShare.None)) { }
}
catch (IOException)
{
//the file is unavailable because it is:
//still being written to
//or being processed by another thread
//or does not exist (has already been processed)
Debug.WriteLine($"File present but locked : {filePath}");
return true;
}
//file is not locked
return false;
}
}
public static class TaskUtilities
{
public static async Task Run(Action action, TimeSpan period, CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(period, cancellationToken);
if (!cancellationToken.IsCancellationRequested)
action();
}
}
public static Task Run(Action action, TimeSpan period)
{
return Run(action, period, CancellationToken.None);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment