Из 2 папок с одинаковыми файлами выбрать 1 файл
Есть 2 папки, в этих папках файлы с именами: 20200620.txt, файлы приходят с интервалом 1 час в обе папки сразу. Бывают сбои и в 1 из папок файл может не попасть.
static void Main(string[] args)
{
FileSystemWatcher mainFolder = new FileSystemWatcher(path: @"C:\Users\Denis\Desktop\Test1", filter: "*.txt")
{
NotifyFilter = NotifyFilters.FileName
};
mainFolder.Created += OnCreateMainFile;
mainFolder.EnableRaisingEvents = true;
FileSystemWatcher spareFolder = new FileSystemWatcher(path: @"C:\Users\Denis\Desktop\Test2", filter: "*.txt")
{
NotifyFilter = NotifyFilters.FileName
};
spareFolder.Created += OnCreateSpareFile;
spareFolder.EnableRaisingEvents = true;
}
private static void OnCreateSpareFile(object sender, FileSystemEventArgs e)
{
Console.WriteLine($"File: {e.FullPath} {e.ChangeType} in Spare Folder");
}
private static void OnCreateMainFile(object sender, FileSystemEventArgs e)
{
Thread.Sleep(5000);
Console.WriteLine($"File: {e.FullPath} {e.ChangeType} in Main Folder");
}
Как мне выбрать из пришедших файлов 1 и с ним работать, а если не пришел во 2 папку файл вообще, тогда работать с 1 файлом?
Ответы (2 шт):
Интересная задачка, убил время, но вроде решение есть, код нужно допиливать, он в тестовом варианте.
- Приходят два файла в две разные папки.
- Приоритет всегда у первой папки.
Вот код с комментами:
class FileFindFolder
{
private bool f1 = false; // чек работы с первой папкой
private string _firstPath = ""; // первый путь
private string _secondPath = ""; // второй путь
/// <summary>
/// Конструкт класса FileFindFolder
/// </summary>
/// <param name="_firstPath">Первый путь до папки</param>
/// <param name="_secondPath">Второй путь до папки</param>
public FileFindFolder(string _firstPath, string _secondPath)
{
this._firstPath = _firstPath;
this._secondPath = _secondPath;
}
/// <summary>
/// Запуск работы программы
/// </summary>
public void Run()
{
LookFileFirst();
LookFileSecond();
Console.WriteLine("Что бы закончить, нажмите любую кнопку");
Console.ReadKey();
}
/// <summary>
/// Слежка за первой папкой
/// </summary>
private void LookFileFirst()
{
FileSystemWatcher _watcher = new FileSystemWatcher(); // можно упростить создание экземпляра
_watcher.Path = _firstPath;
_watcher.Filter = "*.txt";
_watcher.EnableRaisingEvents = true;
_watcher.NotifyFilter = NotifyFilters.FileName;
_watcher.Renamed += OnChanged;
}
/// <summary>
/// Слежка за второй папкой
/// </summary>
private void LookFileSecond()
{
FileSystemWatcher _watcher = new FileSystemWatcher();
_watcher.Path = _secondPath;
_watcher.Filter = "*.txt";
_watcher.EnableRaisingEvents = true;
_watcher.NotifyFilter = NotifyFilters.FileName;
_watcher.Renamed += OnChanged2;
}
/// <summary>
/// Реакция на приход в первую папку файла
/// </summary>
/// <param name="source"></param>
/// <param name="e"></param>
private void OnChanged(object source, FileSystemEventArgs e)
{
Console.WriteLine("Выполняю задачу с первой папкой");
f1 = true;
// Что то сделалось
// присвоить f1 = false
}
/// <summary>
/// Реакция на приход во вторую папку файла
/// </summary>
/// <param name="source"></param>
/// <param name="e"></param>
private void OnChanged2(object source, FileSystemEventArgs e)
{
if (!f1)
{
Console.WriteLine("Выполняю задачу с второй папкой");
} else { Console.WriteLine("Приоритет работы с файлом из первой папки"); }
}
}
Фото выполнения работы:
Код на гите: тык
Я думаю, решением тут может быть некая задержка перед чтением данных, то есть нечто такое:
private const string firstPath = @"D:\Test\1";
private const string secondPath = @"D:\Test\2";
private static readonly List<string> tempPath = new List<string>();
private static CancellationTokenSource tokenSource;
static void Main(string[] args)
{
var watcher = new FileSystemWatcher(@"D:\Test")
{
NotifyFilter = NotifyFilters.FileName,
EnableRaisingEvents = true,
IncludeSubdirectories = true
};
watcher.Created += OnCreated;
Console.ReadKey();
}
private static async void OnCreated(object sender, FileSystemEventArgs e)
{
tokenSource?.Cancel();
tokenSource = new CancellationTokenSource();
tempPath.Add(e.FullPath);
await DelayRead(cancellationToken: tokenSource.Token);
}
private async static Task DelayRead(int delay = 1000, CancellationToken cancellationToken = default)
{
try
{
await Task.Delay(delay, cancellationToken);
var file = tempPath.FirstOrDefault(x => x.Contains(firstPath)) ?? tempPath.FirstOrDefault(x => x.Contains(secondPath));
Console.WriteLine($"Считываем: {file}");
tempPath.Clear();
}
catch (TaskCanceledException)
{
Debug.WriteLine("Задача отменена");
}
}
Поясняю:
- Мы все
FileSystemWatcher(кстати, в вашем случае можно обойтись одним, просто указать емуIncludeSubdirectories) подписываем на одно событие, у меня этоOnCreated. - В этом событие мы
- Отменяем (если есть) старое ожидание.
- Создаем токен отмены для нового ожидания.
- Добавляем полученный путь нового файла во временный список.
- Запускаем задачу чтения, которая должна после ожидания прочитать файл.
- Далее сама задача ожидания, это простой
Task, который имеетTask.Delay()с указанным временем и токеном отмены.- Если токен подал команду на отмену, то задача выдаст
TaskCanceledException, который мы обрабатываем как надо и не выполняем дальше логику. - В переменную
fileя забираю по примитивной логике (которую вам наверно надо будет расширить под свои нужды) из той самой временной коллекции путь сначала тот, который соответствует первой директории и если онNULL, то беру 2-ую директорию. - Полученный путь вывожу на экран (у вас тут чтение и другая логика).
- Очищаю временный список.
- Если токен подал команду на отмену, то задача выдаст
В итоге будет работать так:
- Запускаем ожидание при появление нового файла.
- Если ожидание все еще идет и появился новый файл, то начинаем его по новой.
- Если в течении указанного времени не было создано новых файлов, то берем тот, который подходит под нашу логику.
Я тут не стал сильно нагружать пример лишним, но в комментариях правильно заметили, что тут идет работа с несколькими потоками, а значит стоит себя обезопасить в этом плане. Моя задача была лишь показать задумку, которая могла бы решить данную задачу, а уж как дальше ее примерять уже оставлю на вас.
А кстати, если вам без разницы какой файл открывать первым (главное открыть), то можно тогда вовсе обойтись одним обработчиком, где вы делаете простой флаг, некая bool переменная, которая будет говорить начали вы чтение файла или нет и тогда в момент появления нового файла вы просто берете первый пришедший, читаете его, флаг ставите на true (тем самым исключив повторного запуска чтения из файла со 2-й директории) и по окончанию чтения вы флаг возвращаете обратно на false. Ну либо запоминайте имя последнего прочитанного файла. То есть нечто такое (набросал на глаз, не проверял):
static string lastFile;
private static void OnCreated(object sender, FileSystemEventArgs e)
{
var fileName = Path.GetFileNameWithoutExtension(e.FullPath);
if (lastFile != fileName)
{
lastFile = fileName;
Console.WriteLine($"Считываем: {e.FullPath}");
}
}
