Мне нужно создать службу Windows, которая отслеживает указанную папку на наличие новых файлов, обрабатывает ее и перемещает в другое место.
Я начал с использования FileSystemWatcher
. Мой босс не любит FileSystemWatcher
и хочет, чтобы я использовал опрос с использованием Timer
или любого другого механизма, кроме FileSystemWatcher
.
Как вы можете контролировать каталогизацию, не используя FileSystemWatcher
используя. NET Framework?
Используя ответ @Petoj, я включил полнофункциональную службу Windows, которая каждые пять минут опрашивает новые файлы. Это ограничено, поэтому только один поток опрашивает, учитывает время обработки и поддерживает паузу и своевременную остановку. Он также поддерживает простое присоединение отладчика к system.start
public partial class Service : ServiceBase{
List<string> fileList = new List<string>();
System.Timers.Timer timer;
public Service()
{
timer = new System.Timers.Timer();
//When autoreset is True there are reentrancy problems.
timer.AutoReset = false;
timer.Elapsed += new System.Timers.ElapsedEventHandler(DoStuff);
}
private void DoStuff(object sender, System.Timers.ElapsedEventArgs e)
{
LastChecked = DateTime.Now;
string[] files = System.IO.Directory.GetFiles("c:\\", "*", System.IO.SearchOption.AllDirectories);
foreach (string file in files)
{
if (!fileList.Contains(file))
{
fileList.Add(file);
do_some_processing();
}
}
TimeSpan ts = DateTime.Now.Subtract(LastChecked);
TimeSpan MaxWaitTime = TimeSpan.FromMinutes(5);
if (MaxWaitTime.Subtract(ts).CompareTo(TimeSpan.Zero) > -1)
timer.Interval = MaxWaitTime.Subtract(ts).TotalMilliseconds;
else
timer.Interval = 1;
timer.Start();
}
protected override void OnPause()
{
base.OnPause();
this.timer.Stop();
}
protected override void OnContinue()
{
base.OnContinue();
this.timer.Interval = 1;
this.timer.Start();
}
protected override void OnStop()
{
base.OnStop();
this.timer.Stop();
}
protected override void OnStart(string[] args)
{
foreach (string arg in args)
{
if (arg == "DEBUG_SERVICE")
DebugMode();
}
#if DEBUG
DebugMode();
#endif
timer.Interval = 1;
timer.Start();
}
private static void DebugMode()
{
Debugger.Break();
}
}
На самом деле компонент FileWatcher не является на 100% "стабильным", судя по моему многолетнему опыту. Поместите достаточно файлов в папку, и вы потеряете некоторые события. Это особенно верно, если вы отслеживаете общую папку, даже если вы увеличиваете размер буфера.
Итак, по всем практическим причинам используйте FileWatcher вместе с таймером, который сканирует папку на наличие изменений, для наиболее оптимального решения.
Примеров создания кода Timer должно быть предостаточно, если вы погуглите. Если вы отслеживаете последний DateTime при запуске таймера, проверьте дату изменения каждого файла и сравните ее с датой. Достаточно простая логика.
Интервал таймера зависит от того, насколько срочными являются изменения для вашей системы. Но проверять каждую минуту должно быть хорошо для многих сценариев.
При запуске программы используйте Directory.GetFiles(path) для получения списка файлов.
Затем создайте таймер и в его прошедшем событии вызовите hasNewFiles:
static List<string> hasNewFiles(string path, List<string> lastKnownFiles)
{
List<string> files = Directory.GetFiles(path).ToList();
List<string> newFiles = new List<string>();
foreach (string s in files)
{
if (!lastKnownFiles.Contains(s))
newFiles.Add(s);
}
return new List<string>();
}
В вызывающем коде у вас будут новые файлы, если:
List<string> newFiles = hasNewFiles(path, lastKnownFiles);
if (newFiles.Count > 0)
{
processFiles(newFiles);
lastKnownFiles = newFiles;
}
изменить: если вы хотите более гибкое решение:
static IEnumerable<string> hasNewFiles(string path, List<string> lastKnownFiles)
{
return from f in Directory.GetFiles(path)
where !lastKnownFiles.Contains(f)
select f;
}
List<string> newFiles = hasNewFiles(path, lastKnownFiles);
if (newFiles.Count() > 0)
{
processFiles(newFiles);
lastKnownFiles = newFiles;
}
Вы можете использовать Directory.GetFiles()
:
using System.IO;
var fileList = new List<string>();
foreach (var file in Directory.GetFiles(@"c:\", "*", SearchOption.AllDirectories))
{
if (!fileList.Contains(file))
{
fileList.Add(file);
//do something
}
}
Обратите внимание, что это проверяет только новые файлы, а не измененные файлы, если вам это нужно, используйте FileInfo
Я бы спросил, почему бы не использовать FileSystemWatcher. Он регистрируется в ОС и немедленно получает уведомление, когда событие завершается в файловой системе.
Если вам действительно нужно провести опрос, просто создайте System.Timers.Timer, создайте метод для его вызова и проверьте наличие файла в этом методе.
Да, вы можете создать таймер и подключить обработчик к событию Elapsed, который создаст экземпляр класса DirectoryInfo для просматриваемого вами каталога и вызовет либо GetFiles(), либо EnumerateFiles(). GetFiles() возвращает массив FileInfo[], а EnumerateFiles() возвращает "потоковое" IEnumerable. EnumerateFiles() будет более эффективным, если вы ожидаете, что в этой папке будет много файлов, когда вы просматриваете; вы можете начать работать с IEnumerable до того, как метод получит все FileInfos, в то время как GetFiles заставит вас ждать.
Что касается того, почему это может быть на самом деле лучше, чем FileWatcher, это зависит от архитектуры за кулисами. Возьмем, к примеру, базовый рабочий процесс извлечения/преобразования/проверки/загрузки. Во-первых, такой рабочий процесс может потребовать создания дорогостоящих экземпляров объектов (соединений с БД, экземпляров механизма правил и т. д.). Эти единовременные накладные расходы значительно уменьшаются, если рабочий процесс структурирован для обработки всего, что ему доступно, за один раз. Во-вторых, FileWatcher потребует, чтобы все, что вызывается обработчиками событий, например этот рабочий процесс, было потокобезопасным, поскольку МНОГИЕ события могут выполняться одновременно, если файлы постоянно поступают. Если это невозможно, таймер можно очень легко настроить. чтобы ограничить систему одним запущенным рабочим процессом, заставив обработчики событий проверять потокобезопасный флаг «работа процесса» и просто завершать работу, если другой поток обработчика установил его и еще не завершил. Файлы в папке в это время будут выбраны при следующем срабатывании таймера, в отличие от FileWatcher, где, если вы завершаете обработчик, информация о существовании этого файла теряется.
1) Похоже, ваш начальник идиот
2) Вам придется использовать такие функции, как Directory.GetFiles, File.GetLastAccessTime и т. д., и хранить их в памяти, чтобы проверить, не изменились ли они.