Last active
March 19, 2019 01:53
-
-
Save walterlv/cffec6dd951780ea946feb2ea96f302a to your computer and use it in GitHub Desktop.
FileWatcher that helps you to watch a single file change even if the file or it's owner folders does not exists.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
namespace Walterlv.IO | |
{ | |
public sealed class FileWatcher | |
{ | |
/// <summary>监视的文件。</summary> | |
private readonly FileInfo _file; | |
/// <summary>获取当前监视的文件夹监视器。可能会因为文件(夹)不存在而改变。</summary> | |
private FileSystemWatcher _watcher; | |
public FileWatcher(string fileName) | |
{ | |
var file = fileName ?? throw new ArgumentNullException(nameof(fileName)); | |
_file = new FileInfo(file); | |
} | |
public FileWatcher(FileInfo file) | |
{ | |
_file = file ?? throw new ArgumentNullException(nameof(file)); | |
} | |
public event EventHandler Changed; | |
private void OnChanged() | |
{ | |
Changed?.Invoke(this, EventArgs.Empty); | |
} | |
/// <summary> | |
/// 监视文件的改变。 | |
/// </summary> | |
/// <remarks> | |
/// 此方法可以被重复调用,不会引发异常或导致重复监视。 | |
/// </remarks> | |
public void Watch() | |
{ | |
Stop(); | |
var (directory, file) = FindWatchableLevel(); | |
if (File.Exists(_file.FullName)) | |
{ | |
// 如果文件存在,说明这是最终的文件。 | |
// 注意使用 File.Exists 判断已存在的同名文件夹时会返回 false。 | |
_watcher = new FileSystemWatcher(directory, file) | |
{ | |
EnableRaisingEvents = true, | |
NotifyFilter = NotifyFilters.LastWrite, | |
}; | |
_watcher.Changed += FinalFile_Changed; | |
_watcher.Deleted += FileOrDirectory_CreatedOrDeleted; | |
} | |
else | |
{ | |
// 注意这里的 file 可能是文件也可能是文件夹。 | |
_watcher = new FileSystemWatcher(directory, file) | |
{ | |
EnableRaisingEvents = true, | |
}; | |
_watcher.Created += FileOrDirectory_CreatedOrDeleted; | |
_watcher.Renamed += FileOrDirectory_CreatedOrDeleted; | |
_watcher.Deleted += FileOrDirectory_CreatedOrDeleted; | |
} | |
} | |
/// <summary> | |
/// 停止监视文件的改变。 | |
/// </summary> | |
/// <remarks> | |
/// 此方法可以被重复调用。 | |
/// </remarks> | |
public void Stop() | |
{ | |
// 文件 / 文件夹已经创建,所以之前的监视不需要了。 | |
// 文件 / 文件夹被删除了,所以之前的监视没法儿用了。 | |
// Dispose 之后,这个对象就没用了,事件也不会再引发,所以不需要注销事件。 | |
_watcher?.Dispose(); | |
_watcher = null; | |
} | |
private void FileOrDirectory_CreatedOrDeleted(object sender, FileSystemEventArgs e) | |
{ | |
Watch(); | |
} | |
private void FinalFile_Changed(object sender, FileSystemEventArgs e) | |
{ | |
OnChanged(); | |
} | |
/// <summary> | |
/// 从 <see cref="_file"/> 开始寻找第一层存在的文件夹,返回里面的文件。 | |
/// </summary> | |
private (string directory, string file) FindWatchableLevel() | |
{ | |
var path = _file.FullName; | |
// 如果文件存在,就返回文件所在的文件夹和文件本身。 | |
if (File.Exists(path)) | |
{ | |
return (Path.GetDirectoryName(path), Path.GetFileName(path)); | |
} | |
// 如果文件不存在,但文件夹存在,也是返回文件夹和文件本身。 | |
// 这一点在下面的第一层循环中体现。 | |
// 对于每一层循环。 | |
while (true) | |
{ | |
var directory = Path.GetDirectoryName(path); | |
var file = Path.GetFileName(path); | |
// 检查文件夹是否存在,只要文件夹存在,那么就可以返回。 | |
if (Directory.Exists(directory)) | |
{ | |
return (directory, file); | |
} | |
// 如果连文件夹都不存在,那么就需要查找上一层文件夹。 | |
path = directory; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment