使用FileSystemWatcher的多个文件 [英] Using FileSystemWatcher with multiple files
问题描述
我想用FileSystemWatcher对象以监控移动文件的目录及其子目录。然后我想引发一些代码,当所有的文件已被移动。但我不知道怎么办。我的代码就是将每一个文件被移动时触发,如果用户一次我只希望移动多个文件它的所有文件触发一次。所以基本上我想创建一个列表,一旦所有文件的移动做我想要做的东西到列表...
I want to use FileSystemWatcher to monitor a directory and its subdirectories for files that are moved. And then I want to trigger some code when all the files have been moved. But I don't know how. My code as is will trigger each time a file is moved, and if a user moves several files at once I only want it to trigger once for all files. So basically I want to create a list, and once the moving of all files is done I want to do stuff to that list...
下面的代码:
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的代码,但它不工作(见克里斯的回答我的评论):
UPDATE: Trying to use Chris's code, but it doesn't work (see my comment at Chris's answer):
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:
UPDATE 2:
这根据安德斯的回答试过:
Tried this according to Anders's answer:
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();
}
}
我不得不事件触发移动到最后声明和工程。 ?我不知道是否有某种原因,我不想这样做。
I had to move the event trigger into the finally statement, and that works. I don't know if there is some reason I wouldn't want to do that?
推荐答案
像周杰伦说:定时器大概只有这样,才能团事件。锁可能会矫枉过正,但我不喜欢在多线程情况下(我觉得从fswatcher事件被称为从池中的线程)突变的集合的想法。
Like Jay says: a timer is probably the only way to "group" events. The lock may be overkill but I don't like the idea of mutating a collection in a multithreaded situation (I think the events from the fswatcher are called on threads from a pool).
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的复活,如果它得到一个错误(即绑定错误事件,再现观察者的方法)
Remember to set the buffer size on your fswatcher AND implement "resurrection" of the fswatcher if it gets an error (i.e. bind the error event to a method that recreates the watcher).
编辑:说明,在这个例子中定时器是一个System.Timers.Timer,而不是一个System.Threading.Timer
note, the timer in this example is a System.Timers.Timer, not a System.Threading.Timer
编辑:现在包含错误处理的守望者,处理逻辑
Now contains error handling for the watcher, dispose logic.
这篇关于使用FileSystemWatcher的多个文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!