using System; using System.Globalization; using System.IO; namespace ZeroLevel.Services.FileSystem { public sealed class PeriodicFileSystemWatcher : IDisposable { private long _updateSourceTaskKey; private readonly string _sourceFolder; private readonly string _temporaryFolder; private readonly TimeSpan _period; private readonly Action _callback; public PeriodicFileSystemWatcher(TimeSpan period, string watch_folder, string temp_folder, Action callback) { if (string.IsNullOrWhiteSpace(watch_folder)) { throw new ArgumentNullException(nameof(watch_folder)); } if (callback == null) { throw new ArgumentNullException(nameof(callback)); } _callback = callback; _sourceFolder = watch_folder; _temporaryFolder = temp_folder; _period = period; if (_temporaryFolder.IndexOf(':') < 0) { _temporaryFolder = Path.Combine(Configuration.BaseDirectory, _temporaryFolder); } if (false == Directory.Exists(_temporaryFolder)) { Directory.CreateDirectory(_temporaryFolder); } } public void Start() { _updateSourceTaskKey = Sheduller.RemindEvery(_period, CheckSourceFolder); } public void Stop() { Sheduller.Remove(_updateSourceTaskKey); } private void CheckSourceFolder() { try { foreach (var file in GetFilesFromSource()) { try { Log.Debug($"[PeriodicFileSystemWatcher] Find new file {file}"); if (FSUtils.IsFileLocked(new FileInfo(file))) { continue; } string tempFile; try { tempFile = MoveToTemporary(file); } catch (Exception ex) { Log.SystemError(ex, $"[PeriodicFileSystemWatcher] Failed to attempt to move file '{file}' to temporary directory '{_temporaryFolder}'"); continue; } Log.Debug($"[PeriodicFileSystemWatcher] Handle file {file}"); _callback(new FileMeta(Path.GetFileName(file), tempFile)); File.Delete(tempFile); } catch (Exception ex) { Log.SystemError(ex, $"[PeriodicFileSystemWatcher] Fault proceed file {file}"); } } } catch (Exception ex) { Log.SystemError(ex, $"[PeriodicFileSystemWatcher] Failed to process input directory '{_sourceFolder}'"); } } /// /// Moving a file to a temporary directory /// public string MoveToTemporary(string from) { if (from == null) { throw new ArgumentException("from"); } string tempFile = Path.Combine(_temporaryFolder, Path.GetFileName(from)); if (File.Exists(tempFile)) { tempFile = TrySolveCollision(tempFile); } File.Copy(from, tempFile, false); File.Delete(from); return tempFile; } /// /// Resolving collisions in filenames in the temporary directory /// private static string TrySolveCollision(string file) { if (file == null) { throw new ArgumentNullException("file"); } string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file); string extension = Path.GetExtension(file); string directoryName = Path.GetDirectoryName(file); if (directoryName != null) { int num = 0; do { num++; } while (File.Exists(Path.Combine(directoryName, fileNameWithoutExtension + "_" + num.ToString(CultureInfo.CurrentCulture) + extension))); return Path.Combine(directoryName, fileNameWithoutExtension + "_" + num.ToString(CultureInfo.CurrentCulture) + extension); } throw new ArgumentException("folder"); } /// /// Getting a list of files from the input directory /// public string[] GetFilesFromSource() { string[] files = Directory.GetFiles(_sourceFolder, "*.*", SearchOption.TopDirectoryOnly); Array.Sort(files, FileNameSortCompare); return files; } /// /// File Name Comparison /// private static int FileNameSortCompare(string x, string y) { int num; int num2; if (int.TryParse(Path.GetFileNameWithoutExtension(x), out num) && int.TryParse(Path.GetFileNameWithoutExtension(y), out num2)) { return num - num2; } return string.Compare(x, y, StringComparison.InvariantCultureIgnoreCase); } public void Dispose() { Sheduller.Remove(_updateSourceTaskKey); } } }