我在FileSystemWatcher
中使用C#
类来跟踪文件在修改文件后保存文件时的更改。
FileSystemWatcher watcher = new FileSystemWatcher()
{
Path = DIR_NAME,
NotifyFilter = NotifyFilters.LastWrite |
NotifyFilters.CreationTime |
NotifyFilters.LastAccess,
Filter = "*.pdf",
IncludeSubdirectories = true,
EnableRaisingEvents = true
};
watcher.Changed += OnChanged;
但是,我想要跟踪的文件是以编程方式创建的,如下所示:
FileStream stream = FileUtil.CreateNewFile(filePath); // Creates a file
file.WriteFileToStream(stream); // Writes into the file
理想情况下,我的代码应该运行如下:
OnChanged
,即我希望我的OnChanged
处理程序代码仅在真实用户修改并保存时执行。但是,只要文件以编程方式写入,即在以下行中,它就会被触发:
file.WriteFileToStream(stream);
从技术上讲,这是正确的行为,因为它跟踪文件中的更改。但是,我的业务案例不允许在最初创建和写入文件时执行OnChanged
处理程序代码。
我的问题是,在创建文件并以编程方式第一次写入时,是否有一种解决方法可以跳过OnChanged
调用?
注意:
FileSystemWatcher
。所以我在创建文件后无法注册它。watcher.EnableRaisingEvents = false;
CreateFile();
watcher.EnableRaisingEvents = true;
方法一:
方法二:
创建文件时,使用OnCreated()
事件处理程序将文件名添加到filesToIgnore
列表中。
在OnChanged
事件处理程序中,检查filesToIgnore
列表是否包含文件名。如果是,请将其从列表中删除(下次处理)并从处理程序返回。
private List<string> filesToIgnore = new List<string>();
private void OnCreated(object source, FileSystemEventArgs file)
{
filesToIgnore.Add(file.Name);
}
private void OnChanged(object source, FileSystemEventArgs file)
{
if(filesToIgnore.Contains(file.Name))
{
filesToIgnore.Remove(file.Name);
return;
}
// Code to execute when user saves the file
}
这种方法假设OnCreated()
将始终在OnChanged().
之前触发
这是一个棘手的情况,除了检查文件是否被锁定(假设它有读或写锁)之外,你无能为力
public bool IsFileLocked(string fileName)
{
try
{
using (var stream = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.None))
{
return false;
}
}
catch (IOException)
{
//the file is unavailable
return true;
}
}
前提是当你得到更新时,你检查文件是否被锁定,如果不是,那么你可以松散地假设它被关闭了。
有些事情要考虑
FileAccess.Read
,所以这对于只读文件不会失败。FileSystemWatcher
中删除事件,并且在某些情况下始终不能依赖事件,阅读文档以了解可以删除事件的原因以及如何修复它们。在这些情况下,您可以通过长时间的轮询和一些逻辑来躲避任何落后者(取决于您的情况)我在OnChanged
处理程序中添加了以下代码,它似乎按预期工作。
private void OnChanged(object source, FileSystemEventArgs file)
{
if (file.ChangeType == WatcherChangeTypes.Created)
return;
FileInfo fileInfo = new FileInfo(file.FullPath);
if (fileInfo.CreationTime == fileInfo.LastWriteTime)
return;
// Handle the Changed event
...
}
但是,我不确定我是否遗漏了会导致此情况发生的事情。有什么想法吗?
下面的扩展方法仅允许处理来自用户操作的事件:
public static void OnChangedByUser(this FileSystemWatcher fsw,
FileSystemEventHandler handler)
{
const int TOLERANCE_MSEC = 100;
object locker = new object();
string fileName = null;
Stopwatch stopwatch = new Stopwatch();
fsw.Created += OnFileCreated;
fsw.Changed += OnFileChanged;
fsw.Disposed += OnDisposed;
void OnFileCreated(object sender, FileSystemEventArgs e)
{
lock (locker)
{
fileName = e.Name;
stopwatch.Restart();
}
}
void OnFileChanged(object sender, FileSystemEventArgs e)
{
lock (locker)
{
if (e.Name == fileName && stopwatch.ElapsedMilliseconds < TOLERANCE_MSEC)
{
return; // Ignore this event
}
}
handler.Invoke(sender, e);
}
void OnDisposed(object sender, EventArgs e)
{
fsw.Created -= OnFileCreated;
fsw.Changed -= OnFileChanged;
fsw.Disposed -= OnDisposed;
}
}
用法示例:
var fsw = new FileSystemWatcher(DIR_NAME);
fsw.OnChangedByUser(File_ChangedByUser);
fsw.EnableRaisingEvents = true;
private static void File_ChangedByUser(object sender, FileSystemEventArgs e)
{
// Handle the event
}