Created
July 4, 2018 13:41
-
-
Save stevebeauge/088603022062a683929abb6784451e4d to your computer and use it in GitHub Desktop.
Reliable replacement for FileSystemWatcher
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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