16 changed files with 252 additions and 6 deletions
@ -0,0 +1,15 @@
|
||||
namespace BitPlus.IO.Directory.Watcher; |
||||
|
||||
public sealed class DirectoryFileEventArgs : EventArgs |
||||
{ |
||||
public string FullPath { get; } |
||||
public EventMode Mode { get; } |
||||
public string? OldFullPath { get; } |
||||
|
||||
public DirectoryFileEventArgs(string fullPath, EventMode mode, string? oldFullPath = null) |
||||
{ |
||||
this.FullPath = fullPath; |
||||
this.Mode = mode; |
||||
this.OldFullPath = oldFullPath; |
||||
} |
||||
} |
||||
@ -1,6 +1,125 @@
|
||||
namespace BitPlus.IO.Directory.Watcher; |
||||
using System.Collections.Concurrent; |
||||
using Dir = System.IO.Directory; |
||||
|
||||
public class DirectoryWatcher |
||||
namespace BitPlus.IO.Directory.Watcher; |
||||
|
||||
public sealed class DirectoryWatcher : IDisposable |
||||
{ |
||||
private readonly FileSystemWatcher _watcher; |
||||
private readonly ConcurrentDictionary<string, DateTimeOffset> _lastEventTimes = new(); |
||||
private readonly TimeSpan _dedupeWindow = TimeSpan.FromMilliseconds(200); |
||||
private bool _disposed; |
||||
|
||||
public string TargetDirectory { get; } |
||||
public bool IncludeSubdirectories |
||||
{ |
||||
get => this._watcher.IncludeSubdirectories; |
||||
set => this._watcher.IncludeSubdirectories = value; |
||||
} |
||||
|
||||
public event EventHandler<DirectoryFileEventArgs>? FileCreated; |
||||
public event EventHandler<DirectoryFileEventArgs>? FileChanged; |
||||
public event EventHandler<DirectoryFileEventArgs>? FileDeleted; |
||||
public event EventHandler<DirectoryFileEventArgs>? FileRenamed; |
||||
public event EventHandler<ErrorEventArgs>? WatcherError; |
||||
|
||||
public DirectoryWatcher(string targetDirectory, string filter = "*.*") |
||||
{ |
||||
if (string.IsNullOrWhiteSpace(targetDirectory)) |
||||
throw new ArgumentException("Target directory cannot be empty.", nameof(targetDirectory)); |
||||
|
||||
if (!Dir.Exists(targetDirectory)) |
||||
throw new DirectoryNotFoundException(targetDirectory); |
||||
|
||||
this.TargetDirectory = targetDirectory; |
||||
|
||||
this._watcher = new FileSystemWatcher(this.TargetDirectory, filter) |
||||
{ |
||||
NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.CreationTime, |
||||
IncludeSubdirectories = false, |
||||
EnableRaisingEvents = false |
||||
}; |
||||
|
||||
this._watcher.Created += this.OnCreated; |
||||
this._watcher.Changed += this.OnChanged; |
||||
this._watcher.Deleted += this.OnDeleted; |
||||
this._watcher.Renamed += this.OnRenamed; |
||||
this._watcher.Error += this.OnError; |
||||
} |
||||
|
||||
public void Start() |
||||
{ |
||||
this.ThrowIfDisposed(); |
||||
this._watcher.EnableRaisingEvents = true; |
||||
} |
||||
|
||||
public void Stop() |
||||
{ |
||||
this.ThrowIfDisposed(); |
||||
this._watcher.EnableRaisingEvents = false; |
||||
} |
||||
|
||||
private void OnCreated(object? sender, FileSystemEventArgs e) |
||||
{ |
||||
if (this.ShouldRaise(e.FullPath)) |
||||
FileCreated?.Invoke(this, new DirectoryFileEventArgs(e.FullPath, EventMode.Created)); |
||||
} |
||||
|
||||
private void OnChanged(object? sender, FileSystemEventArgs e) |
||||
{ |
||||
if (this.ShouldRaise(e.FullPath)) |
||||
FileChanged?.Invoke(this, new DirectoryFileEventArgs(e.FullPath, EventMode.Changed)); |
||||
} |
||||
|
||||
private void OnDeleted(object? sender, FileSystemEventArgs e) |
||||
{ |
||||
if (this.ShouldRaise(e.FullPath)) |
||||
FileDeleted?.Invoke(this, new DirectoryFileEventArgs(e.FullPath, EventMode.Deleted)); |
||||
} |
||||
|
||||
private void OnRenamed(object? sender, RenamedEventArgs e) |
||||
{ |
||||
if (this.ShouldRaise(e.FullPath)) |
||||
{ |
||||
var args = new DirectoryFileEventArgs(e.FullPath, EventMode.Renamed, e.OldFullPath); |
||||
FileChanged?.Invoke(this, args); |
||||
FileRenamed?.Invoke(this, args); |
||||
} |
||||
} |
||||
|
||||
private void OnError(object? sender, ErrorEventArgs e) |
||||
=> WatcherError?.Invoke(this, e); |
||||
|
||||
private bool ShouldRaise(string fullPath) |
||||
{ |
||||
var now = DateTimeOffset.UtcNow; |
||||
|
||||
if (this._lastEventTimes.TryGetValue(fullPath, out var last)) |
||||
{ |
||||
if (now - last < this._dedupeWindow) |
||||
return false; |
||||
} |
||||
|
||||
this._lastEventTimes[fullPath] = now; |
||||
return true; |
||||
} |
||||
|
||||
private void ThrowIfDisposed() |
||||
{ |
||||
if (this._disposed) |
||||
throw new ObjectDisposedException(nameof(DirectoryWatcher)); |
||||
} |
||||
|
||||
public void Dispose() |
||||
{ |
||||
if (this._disposed) return; |
||||
this._disposed = true; |
||||
|
||||
} |
||||
this._watcher.Created -= this.OnCreated; |
||||
this._watcher.Changed -= this.OnChanged; |
||||
this._watcher.Deleted -= this.OnDeleted; |
||||
this._watcher.Renamed -= this.OnRenamed; |
||||
this._watcher.Error -= this.OnError; |
||||
this._watcher.Dispose(); |
||||
} |
||||
} |
||||
@ -0,0 +1,9 @@
|
||||
namespace BitPlus.IO.Directory.Watcher; |
||||
|
||||
public enum EventMode |
||||
{ |
||||
Created, |
||||
Changed, |
||||
Deleted, |
||||
Renamed |
||||
} |
||||
@ -0,0 +1,23 @@
|
||||
{ |
||||
"runtimeTarget": { |
||||
"name": ".NETCoreApp,Version=v10.0", |
||||
"signature": "" |
||||
}, |
||||
"compilationOptions": {}, |
||||
"targets": { |
||||
".NETCoreApp,Version=v10.0": { |
||||
"BitPlus.IO.Directory.Watcher/1.0.0": { |
||||
"runtime": { |
||||
"BitPlus.IO.Directory.Watcher.dll": {} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
"libraries": { |
||||
"BitPlus.IO.Directory.Watcher/1.0.0": { |
||||
"type": "project", |
||||
"serviceable": false, |
||||
"sha512": "" |
||||
} |
||||
} |
||||
} |
||||
Binary file not shown.
Binary file not shown.
@ -1,7 +1,34 @@
|
||||
internal class Program |
||||
using BitPlus.IO.Directory.Watcher; |
||||
|
||||
internal class Program |
||||
{ |
||||
private static void Main(string[] args) |
||||
{ |
||||
Console.WriteLine("Hello, World!"); |
||||
var path = args.Length > 0 ? args[0] : null; |
||||
|
||||
while (string.IsNullOrWhiteSpace(path) || !Directory.Exists(path)) |
||||
{ |
||||
Console.Write("감시할 폴더 경로를 입력하세요(Enter the folder path to monitor.): "); |
||||
path = Console.ReadLine(); |
||||
|
||||
if (string.IsNullOrWhiteSpace(path)) |
||||
Console.WriteLine("경로가 비어 있습니다.(The path is empty.)"); |
||||
else if (!Directory.Exists(path)) |
||||
Console.WriteLine("폴더가 존재하지 않습니다.(The folder does not exist.)"); |
||||
} |
||||
|
||||
using var watcher = new DirectoryWatcher(path); |
||||
watcher.FileCreated += (_, e) => Console.WriteLine($"[CREATE] {e.FullPath}"); |
||||
watcher.FileChanged += (_, e) => Console.WriteLine($"[CHANGE] {e.FullPath}"); |
||||
watcher.FileDeleted += (_, e) => Console.WriteLine($"[DELETE] {e.FullPath}"); |
||||
watcher.FileRenamed += (_, e) => Console.WriteLine($"[RENAME] {e.OldFullPath} -> {e.FullPath}"); |
||||
watcher.WatcherError += (_, e) => Console.WriteLine($"[ERROR] {e.GetException().Message}"); |
||||
|
||||
watcher.Start(); |
||||
Console.WriteLine($"감시 시작(Start the surveillance): {path}"); |
||||
Console.WriteLine("종료하려면 Enter를 누르세요...(Press Enter to exit)"); |
||||
|
||||
Console.ReadLine(); |
||||
watcher.Stop(); |
||||
} |
||||
} |
||||
Binary file not shown.
Binary file not shown.
@ -0,0 +1,39 @@
|
||||
{ |
||||
"runtimeTarget": { |
||||
"name": ".NETCoreApp,Version=v10.0", |
||||
"signature": "" |
||||
}, |
||||
"compilationOptions": {}, |
||||
"targets": { |
||||
".NETCoreApp,Version=v10.0": { |
||||
"Sample/1.0.0": { |
||||
"dependencies": { |
||||
"BitPlus.IO.Directory.Watcher": "1.0.0" |
||||
}, |
||||
"runtime": { |
||||
"Sample.dll": {} |
||||
} |
||||
}, |
||||
"BitPlus.IO.Directory.Watcher/1.0.0": { |
||||
"runtime": { |
||||
"BitPlus.IO.Directory.Watcher.dll": { |
||||
"assemblyVersion": "1.0.0.0", |
||||
"fileVersion": "1.0.0.0" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
"libraries": { |
||||
"Sample/1.0.0": { |
||||
"type": "project", |
||||
"serviceable": false, |
||||
"sha512": "" |
||||
}, |
||||
"BitPlus.IO.Directory.Watcher/1.0.0": { |
||||
"type": "project", |
||||
"serviceable": false, |
||||
"sha512": "" |
||||
} |
||||
} |
||||
} |
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,12 @@
|
||||
{ |
||||
"runtimeOptions": { |
||||
"tfm": "net10.0", |
||||
"framework": { |
||||
"name": "Microsoft.NETCore.App", |
||||
"version": "10.0.0" |
||||
}, |
||||
"configProperties": { |
||||
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false |
||||
} |
||||
} |
||||
} |
||||
@ -1,4 +1,6 @@
|
||||
# DirectoryWatcher |
||||
|
||||
디렉터리를 감시 및 변경에 따른 이벤트 처리기 |
||||
Event handlers for monitoring and changing directories |
||||
Event handlers for monitoring and changing directories |
||||
|
||||
 |
||||
|
After Width: | Height: | Size: 76 KiB |
Loading…
Reference in new issue