in dotnet/src/Azure.Iot.Operations.Services/Assets/FilesMonitor/FilesMonitor.cs [33:128]
internal void Start()
{
if (_startedObserving)
{
return;
}
_startedObserving = true;
_observationTaskCancellationTokenSource = new();
var observationTask = new Task(
async () =>
{
try
{
while (!_observationTaskCancellationTokenSource.Token.IsCancellationRequested)
{
string directoryToObserve = _directoryRetriever.Invoke();
if (string.IsNullOrWhiteSpace(directoryToObserve) || !Directory.Exists(directoryToObserve))
{
// The folder was deleted, so all previously known files must have been deleted as well
foreach (string filePath in _lastKnownDirectoryState.Keys)
{
OnFileChanged?.Invoke(this, new FileChangedEventArgs(filePath, ChangeType.Deleted));
}
_lastKnownDirectoryState.Clear();
}
else
{
var currentFilesInDirectory = Directory.EnumerateFiles(directoryToObserve);
// Check if any previously known files are gone now
List<string> filePathsToRemove = new();
foreach (string filePath in _lastKnownDirectoryState.Keys)
{
if (!currentFilesInDirectory.Contains(filePath))
{
filePathsToRemove.Add(filePath);
}
}
foreach (string filePathToRemove in filePathsToRemove)
{
_lastKnownDirectoryState.Remove(filePathToRemove);
OnFileChanged?.Invoke(this, new FileChangedEventArgs(filePathToRemove, ChangeType.Deleted));
}
// Check if any previously known files were updated or if any unknown files have been added to this directory
foreach (string filePath in currentFilesInDirectory)
{
try
{
//TODO need testing on file create/delete cases
if (!_lastKnownDirectoryState.ContainsKey(filePath))
{
await SaveFileStateAsync(filePath);
OnFileChanged?.Invoke(this, new FileChangedEventArgs(filePath, ChangeType.Created));
}
else
{
byte[] contents = await FileUtilities.ReadFileWithRetryAsync(filePath);
byte[] contentsHash = SHA1.HashData(contents);
if (!_lastKnownDirectoryState[filePath].SequenceEqual(contentsHash))
{
_lastKnownDirectoryState[filePath] = contentsHash;
OnFileChanged?.Invoke(this, new FileChangedEventArgs(filePath, ChangeType.Updated));
}
}
}
catch (IOException e)
{
// File may have been accessed by another process. Ignore error and try again.
Trace.TraceWarning("Failed to access file with path {0} due to error {1}", filePath, e);
}
}
}
await Task.Delay(_pollingInterval);
}
}
catch (ObjectDisposedException)
{
// The cancellation token used to control this thread has been disposed. End this thread gracefully
}
finally
{
_startedObserving = false;
}
});
observationTask.Start();
}