Add FolderWatcherService - PDF-Druck per Ordnerüberwachung

This commit is contained in:
administrator 2026-04-19 21:42:40 +02:00
parent 6720adfd96
commit 92e2767f76
4 changed files with 169 additions and 3 deletions

View file

@ -21,6 +21,9 @@ public class MailPrintOptions
public List<string> BlockedSenders { get; set; } = new(); public List<string> BlockedSenders { get; set; } = new();
public WebApiOptions WebApi { get; set; } = new(); public WebApiOptions WebApi { get; set; } = new();
/// <summary>Ordner-Überwachung: PDFs in diesen Ordnern werden automatisch gedruckt.</summary>
public List<FolderWatcher> FolderWatchers { get; set; } = new();
} }
public class PrinterProfile public class PrinterProfile
@ -54,3 +57,15 @@ public class WebApiOptions
public bool BindAllInterfaces { get; set; } = false; public bool BindAllInterfaces { get; set; } = false;
public string ApiKey { get; set; } = ""; public string ApiKey { get; set; } = "";
} }
public class FolderWatcher
{
public string Name { get; set; } = "";
/// <summary>Zu überwachender Ordner (vollständiger Pfad)</summary>
public string Path { get; set; } = "";
/// <summary>Auch Unterordner überwachen</summary>
public bool IncludeSubfolders { get; set; } = false;
/// <summary>Datei nach erfolgreichem Druck löschen</summary>
public bool DeleteAfterPrint { get; set; } = true;
public string PrinterProfileName { get; set; } = "";
}

View file

@ -8,29 +8,38 @@ public class MailPrintWorker : BackgroundService
private readonly ILogger<MailPrintWorker> _logger; private readonly ILogger<MailPrintWorker> _logger;
private readonly MailFetchService _mailService; private readonly MailFetchService _mailService;
private readonly PrintService _printService; private readonly PrintService _printService;
private readonly FolderWatcherService _folderService;
private readonly MailPrintOptions _options; private readonly MailPrintOptions _options;
public MailPrintWorker(ILogger<MailPrintWorker> logger, MailFetchService mailService, public MailPrintWorker(
PrintService printService, IOptions<MailPrintOptions> options) ILogger<MailPrintWorker> logger,
MailFetchService mailService,
PrintService printService,
FolderWatcherService folderService,
IOptions<MailPrintOptions> options)
{ {
_logger = logger; _logger = logger;
_mailService = mailService; _mailService = mailService;
_printService = printService; _printService = printService;
_folderService = folderService;
_options = options.Value; _options = options.Value;
} }
protected override async Task ExecuteAsync(CancellationToken ct) protected override async Task ExecuteAsync(CancellationToken ct)
{ {
// Ordner-Überwachung starten (event-basiert, läuft dauerhaft)
_folderService.Start();
if (_options.Accounts.Count == 0) if (_options.Accounts.Count == 0)
{ {
_logger.LogWarning("Keine Postfächer konfiguriert Mail-Polling deaktiviert."); _logger.LogWarning("Keine Postfächer konfiguriert Mail-Polling deaktiviert.");
await Task.Delay(Timeout.Infinite, ct).ConfigureAwait(false);
return; return;
} }
_logger.LogInformation("{Count} Postfach/Postfächer konfiguriert, Intervall: {Interval}s", _logger.LogInformation("{Count} Postfach/Postfächer konfiguriert, Intervall: {Interval}s",
_options.Accounts.Count, _options.PollIntervalSeconds); _options.Accounts.Count, _options.PollIntervalSeconds);
// Pro Account einen eigenen Poll-Loop starten
var tasks = _options.Accounts.Select(account => PollAccountAsync(account, ct)); var tasks = _options.Accounts.Select(account => PollAccountAsync(account, ct));
await Task.WhenAll(tasks); await Task.WhenAll(tasks);
} }

View file

@ -46,6 +46,7 @@ try
builder.Services.Configure<MailPrintOptions>(builder.Configuration.GetSection("MailPrint")); builder.Services.Configure<MailPrintOptions>(builder.Configuration.GetSection("MailPrint"));
builder.Services.AddSingleton<PrintService>(); builder.Services.AddSingleton<PrintService>();
builder.Services.AddSingleton<MailFetchService>(); builder.Services.AddSingleton<MailFetchService>();
builder.Services.AddSingleton<FolderWatcherService>();
builder.Services.AddHostedService<MailPrintWorker>(); builder.Services.AddHostedService<MailPrintWorker>();
builder.Services.AddControllers(); builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddEndpointsApiExplorer();

View file

@ -0,0 +1,141 @@
using Microsoft.Extensions.Options;
namespace MailPrint.Services;
public class FolderWatcherService : IDisposable
{
private readonly ILogger<FolderWatcherService> _logger;
private readonly PrintService _printService;
private readonly MailPrintOptions _options;
private readonly List<FileSystemWatcher> _watchers = new();
public FolderWatcherService(
ILogger<FolderWatcherService> logger,
PrintService printService,
IOptions<MailPrintOptions> options)
{
_logger = logger;
_printService = printService;
_options = options.Value;
}
public void Start()
{
if (_options.FolderWatchers.Count == 0)
{
_logger.LogInformation("Keine Ordner-Überwachung konfiguriert.");
return;
}
foreach (var config in _options.FolderWatchers)
{
if (string.IsNullOrEmpty(config.Path) || !Directory.Exists(config.Path))
{
_logger.LogWarning("[{Name}] Ordner nicht gefunden: {Path}", config.Name, config.Path);
continue;
}
// Beim Start bereits vorhandene PDFs drucken
PrintExistingFiles(config);
var watcher = new FileSystemWatcher(config.Path)
{
Filter = "*.pdf",
IncludeSubdirectories = config.IncludeSubfolders,
NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite,
EnableRaisingEvents = true
};
watcher.Created += (_, e) => OnFileDetected(e.FullPath, config);
watcher.Renamed += (_, e) =>
{
if (e.Name?.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase) == true)
OnFileDetected(e.FullPath, config);
};
_watchers.Add(watcher);
_logger.LogInformation("[{Name}] Überwache: {Path} (Unterordner: {Sub})",
config.Name, config.Path, config.IncludeSubfolders);
}
}
private void PrintExistingFiles(FolderWatcher config)
{
var files = Directory.GetFiles(config.Path, "*.pdf",
config.IncludeSubfolders ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
if (files.Length == 0) return;
_logger.LogInformation("[{Name}] {Count} vorhandene PDF(s) gefunden werden gedruckt.", config.Name, files.Length);
foreach (var file in files)
OnFileDetected(file, config);
}
private void OnFileDetected(string filePath, FolderWatcher config)
{
// Kurz warten bis Datei vollständig geschrieben ist
if (!WaitForFile(filePath))
{
_logger.LogWarning("[{Name}] Datei nicht lesbar (gesperrt?): {File}", config.Name, filePath);
return;
}
_logger.LogInformation("[{Name}] Neue Datei: {File}", config.Name, filePath);
try
{
_printService.PrintPdf(new PrintJob
{
FilePath = filePath,
MailSubject = Path.GetFileName(filePath),
MailFrom = $"Ordner:{config.Name}",
PrinterProfileName = config.PrinterProfileName
});
if (config.DeleteAfterPrint)
{
try
{
File.Delete(filePath);
_logger.LogInformation("[{Name}] Gelöscht: {File}", config.Name, filePath);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "[{Name}] Löschen fehlgeschlagen: {File}", config.Name, filePath);
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, "[{Name}] Druckfehler: {File}", config.Name, filePath);
}
}
private static bool WaitForFile(string path, int timeoutMs = 5000)
{
var sw = System.Diagnostics.Stopwatch.StartNew();
while (sw.ElapsedMilliseconds < timeoutMs)
{
try
{
using var fs = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.None);
return true;
}
catch (IOException)
{
Thread.Sleep(200);
}
}
return false;
}
public void Dispose()
{
foreach (var w in _watchers)
{
w.EnableRaisingEvents = false;
w.Dispose();
}
_watchers.Clear();
}
}