Add FolderWatcherService - PDF-Druck per Ordnerüberwachung
This commit is contained in:
parent
6720adfd96
commit
92e2767f76
4 changed files with 169 additions and 3 deletions
|
|
@ -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; } = "";
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
141
MailPrint/Services/FolderWatcherService.cs
Normal file
141
MailPrint/Services/FolderWatcherService.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue