[英]Using FileSystemWatcher with multiple files
我想使用FileSystemWatcher來監視目錄及其子目錄中移動的文件。 然后我想在移動所有文件時觸發一些代碼。 但我不知道怎么做。 我的代碼將在每次移動文件時觸發,如果用戶一次移動多個文件,我只希望它為所有文件觸發一次。 所以基本上我想要創建一個列表,一旦完成所有文件的移動,我想對該列表做一些事情......
這是代碼:
class Monitor
{
private List<string> _filePaths;
public void CreateWatcher(string path)
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Filter = "*.*";
watcher.Created += new
FileSystemEventHandler(watcher_FileCreated);
watcher.Path = path;
watcher.IncludeSubdirectories = true;
watcher.EnableRaisingEvents = true;
}
void watcher_FileCreated(object sender, FileSystemEventArgs e)
{
_filePaths.Add(e.FullPath);
Console.WriteLine("Files have been created or moved!");
}
}
更新:嘗試使用Chris的代碼,但它不起作用(請參閱我在Chris的回答中的評論):
class Monitor
{
private List<string> _filePaths;
private Timer _notificationTimer;
private FileSystemWatcher _fsw;
public Monitor(string path)
{
_notificationTimer = new Timer();
_notificationTimer.Elapsed += notificationTimer_Elapsed;
// CooldownSeconds is the number of seconds the Timer is 'extended' each time a file is added.
// I found it convenient to put this value in an app config file.
int CooldownSeconds = 1;
_notificationTimer.Interval = CooldownSeconds * 1000;
_fsw = new FileSystemWatcher();
_fsw.Path = path;
_fsw.IncludeSubdirectories = true;
_fsw.EnableRaisingEvents = true;
// Set up the particulars of your FileSystemWatcher.
_fsw.Created += fsw_Created;
}
private void notificationTimer_Elapsed(object sender, ElapsedEventArgs e)
{
//
// Do what you want to do with your List of files.
//
Console.Write("Done");
// Stop the timer and wait for the next batch of files.
_notificationTimer.Stop();
// Clear your file List.
_filePaths = new List<string>();
}
// Fires when a file is created.
private void fsw_Created(object sender, FileSystemEventArgs e)
{
// Add to our List of files.
_filePaths.Add(e.Name);
// 'Reset' timer.
_notificationTimer.Stop();
_notificationTimer.Start();
}
}
更新2:
根據安德斯的回答試過這個:
public class FileListEventArgs : EventArgs
{
public List<string> FileList { get; set; }
}
public class Monitor
{
private List<string> filePaths;
private ReaderWriterLockSlim rwlock;
private Timer processTimer;
public event EventHandler FileListCreated;
public void OnFileListCreated(FileListEventArgs e)
{
if (FileListCreated != null)
FileListCreated(this, e);
}
public Monitor(string path)
{
filePaths = new List<string>();
rwlock = new ReaderWriterLockSlim();
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Filter = "*.*";
watcher.Created += watcher_FileCreated;
watcher.Path = path;
watcher.IncludeSubdirectories = true;
watcher.EnableRaisingEvents = true;
}
private void ProcessQueue()
{
List<string> list = new List<string>();
try
{
Console.WriteLine("Processing queue, " + filePaths.Count + " files created:");
rwlock.EnterReadLock();
}
finally
{
if (processTimer != null)
{
processTimer.Stop();
processTimer.Dispose();
processTimer = null;
OnFileListCreated(new FileListEventArgs { FileList = filePaths });
filePaths.Clear();
}
rwlock.ExitReadLock();
}
}
void watcher_FileCreated(object sender, FileSystemEventArgs e)
{
try
{
rwlock.EnterWriteLock();
filePaths.Add(e.FullPath);
if (processTimer == null)
{
// First file, start timer.
processTimer = new Timer(2000);
processTimer.Elapsed += (o, ee) => ProcessQueue();
processTimer.Start();
}
else
{
// Subsequent file, reset timer.
processTimer.Stop();
processTimer.Start();
}
}
finally
{
rwlock.ExitWriteLock();
}
}
我不得不將事件觸發器移動到finally語句中,這是有效的。 我不知道是否有某種原因我不想那樣做?
就像傑伊所說:計時器可能是“分組”事件的唯一方式。 鎖定可能有點過分,但我不喜歡在多線程情況下改變集合的想法(我認為來自fswatcher的事件是在池中的線程上調用的)。
public class Monitor : IDisposable
{
private List<string> filePaths;
private ReaderWriterLockSlim rwlock;
private Timer processTimer;
private string watchedPath;
private FileSystemWatcher watcher;
public Monitor(string watchedPath)
{
filePaths = new List<string>();
rwlock = new ReaderWriterLockSlim();
this.watchedPath = watchedPath;
InitFileSystemWatcher();
}
private void InitFileSystemWatcher()
{
watcher = new FileSystemWatcher();
watcher.Filter = "*.*";
watcher.Created += Watcher_FileCreated;
watcher.Error += Watcher_Error;
watcher.Path = watchedPath;
watcher.IncludeSubdirectories = true;
watcher.EnableRaisingEvents = true;
}
private void Watcher_Error(object sender, ErrorEventArgs e)
{
// Watcher crashed. Re-init.
InitFileSystemWatcher();
}
private void Watcher_FileCreated(object sender, FileSystemEventArgs e)
{
try
{
rwlock.EnterWriteLock();
filePaths.Add(e.FullPath);
if (processTimer == null)
{
// First file, start timer.
processTimer = new Timer(2000);
processTimer.Elapsed += ProcessQueue;
processTimer.Start();
}
else
{
// Subsequent file, reset timer.
processTimer.Stop();
processTimer.Start();
}
}
finally
{
rwlock.ExitWriteLock();
}
}
private void ProcessQueue(object sender, ElapsedEventArgs args)
{
try
{
Console.WriteLine("Processing queue, " + filePaths.Count + " files created:");
rwlock.EnterReadLock();
foreach (string filePath in filePaths)
{
Console.WriteLine(filePath);
}
filePaths.Clear();
}
finally
{
if (processTimer != null)
{
processTimer.Stop();
processTimer.Dispose();
processTimer = null;
}
rwlock.ExitReadLock();
}
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (rwlock != null)
{
rwlock.Dispose();
rwlock = null;
}
if (watcher != null)
{
watcher.EnableRaisingEvents = false;
watcher.Dispose();
watcher = null;
}
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
請記住在fswatcher上設置緩沖區大小並實現fswatcher的“復活”(如果它出錯)(即將錯誤事件綁定到重新創建觀察者的方法)。
編輯:注意,此示例中的計時器是System.Timers.Timer,而不是System.Threading.Timer
編輯:現在包含觀察者的錯誤處理,處置邏輯。
我不得不做同樣的事情。 在Monitor類中使用System.Timers.Timer並對其Elapsed事件進行編碼以處理文件列表並清除List。 當第一項通過FSW事件添加到文件列表中時,啟動計時器。 當后續項目添加到列表'重置'時,通過停止並重新啟動它。
像這樣的東西:
class Monitor
{
FileSystemWatcher _fsw;
Timer _notificationTimer;
List<string> _filePaths = new List<string>();
public Monitor() {
_notificationTimer = new Timer();
_notificationTimer.Elapsed += notificationTimer_Elapsed;
// CooldownSeconds is the number of seconds the Timer is 'extended' each time a file is added.
// I found it convenient to put this value in an app config file.
_notificationTimer.Interval = CooldownSeconds * 1000;
_fsw = new FileSystemWatcher();
// Set up the particulars of your FileSystemWatcher.
_fsw.Created += fsw_Created;
}
private void notificationTimer_Elapsed(object sender, ElapsedEventArgs e) {
//
// Do what you want to do with your List of files.
//
// Stop the timer and wait for the next batch of files.
_notificationTimer.Stop();
// Clear your file List.
_filePaths = new List<string>();
}
// Fires when a file is created.
private void fsw_Created(object sender, FileSystemEventArgs e) {
// Add to our List of files.
_filePaths.Add(e.Name);
// 'Reset' timer.
_notificationTimer.Stop();
_notificationTimer.Start();
}
}
Rx - 油門 - 使這項工作變得簡單。 這是一個可測試的,可重復使用的解決方案。
public class Files
{
public static FileSystemWatcher WatchForChanges(string path, string filter, Action triggeredAction)
{
var monitor = new FileSystemWatcher(path, filter);
//monitor.NotifyFilter = NotifyFilters.FileName;
monitor.Changed += (o, e) => triggeredAction.Invoke();
monitor.Created += (o, e) => triggeredAction.Invoke();
monitor.Renamed += (o, e) => triggeredAction.Invoke();
monitor.EnableRaisingEvents = true;
return monitor;
}
}
允許將事件合並為單個序列
public IObservable<Unit> OnUpdate(string path, string pattern)
{
return Observable.Create<Unit>(o =>
{
var watcher = Files.WatchForChanges(path, pattern, () => o.OnNext(new Unit()));
return watcher;
});
}
最后,用法
OnUpdate(path, "*.*").Throttle(Timespan.FromSeconds(10)).Subscribe(this, _ => DoWork())
你顯然可以使用油門速度來滿足你的需求。
此外,將緩沖區大小設置為大於默認值以避免緩沖區溢出。 如果在源目錄中刪除了超過25個文件(在我的測試中),就會發生這種情況。 如果刪除了200個文件,則僅調用幾個文件的事件處理程序,而不是所有文件。
_watcher.InternalBufferSize = 65536; //最大緩沖區大小
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.