feat: 新增可配置的文件库扫描日志记录及缩略图清理功能

- 为文件库的定时轮询及根目录扫描过程,新增扫描生​​命周期日志
- 允许通过 `appsettings` 配置文件,自定义定时扫描的轮询间隔
- 当检测到媒体文件已被删除时,自动清理过期的缩略图映射记录及对应的缩略图文件
This commit is contained in:
2026-05-22 20:09:22 +08:00
parent 6ef410fdfa
commit 27e4029f4a
7 changed files with 125 additions and 18 deletions
@@ -52,6 +52,7 @@ namespace FileShare_API.Configuration
services.AddScoped<IFileLibraryEndpointService, FileLibraryEndpointService>();
services.AddScoped<IFileStreamService, FileStreamService>();
services.AddScoped<IQrCodeService, QrCodeService>();
services.Configure<FileLibraryScanOptions>(configuration.GetSection(nameof(FileLibraryScanOptions)));
services.AddHostedService<FileLibraryScanHostedService>();
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(Path.Combine(AppContext.BaseDirectory, "data-protection-keys")));
+1
View File
@@ -2,6 +2,7 @@
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ActiveDebugProfile>http</ActiveDebugProfile>
<NameOfLastUsedPublishProfile>D:\Project\FileShare\FileShare-API\Properties\PublishProfiles\FolderProfile.pubxml</NameOfLastUsedPublishProfile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebuggerFlavor>ProjectDebugger</DebuggerFlavor>
@@ -1,27 +1,30 @@
using FileShare_Services.Services.FileLibrary;
using Microsoft.Extensions.Options;
namespace FileShare_API.Services
{
/// <summary>
/// 文件库定时扫描后台服务,每分钟执行一次扫描,检查是否有需要扫描的文件库目录。
/// 文件库定时扫描后台服务,按配置间隔检查是否有需要扫描的文件库目录。
/// </summary>
public sealed class FileLibraryScanHostedService(IServiceScopeFactory scopeFactory, ILogger<FileLibraryScanHostedService> logger)
public sealed class FileLibraryScanHostedService(
IServiceScopeFactory scopeFactory,
IOptions<FileLibraryScanOptions> options,
ILogger<FileLibraryScanHostedService> logger)
: BackgroundService
{
/// <summary>
/// 扫描间隔,固定为 1 分钟。
/// </summary>
private static readonly TimeSpan Interval = TimeSpan.FromMinutes(1);
/// <summary>后台服务检查到期扫描任务的轮询间隔。</summary>
private readonly TimeSpan _interval = TimeSpan.FromMinutes(Math.Max(1, options.Value.PollingIntervalMinutes));
/// <summary>
/// 启动扫描循环,首次立即执行,之后按 <see cref="Interval"/> 周期重复执行。
/// 启动扫描循环,首次立即执行,之后按配置的轮询间隔重复执行。
/// </summary>
/// <param name="stoppingToken">应用关闭时触发的取消令牌。</param>
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
logger.LogInformation("文件库定时扫描服务已启动,轮询间隔 {IntervalMinutes} 分钟。", _interval.TotalMinutes);
await ScanAsync(stoppingToken);
using var timer = new PeriodicTimer(Interval);
using var timer = new PeriodicTimer(_interval);
while (await timer.WaitForNextTickAsync(stoppingToken))
{
await ScanAsync(stoppingToken);
@@ -34,14 +37,21 @@ namespace FileShare_API.Services
/// <param name="cancellationToken">取消令牌。</param>
private async Task ScanAsync(CancellationToken cancellationToken)
{
var startedAt = DateTime.UtcNow;
logger.LogInformation("文件库定时扫描轮询开始。");
try
{
await using var scope = scopeFactory.CreateAsyncScope();
var scanner = scope.ServiceProvider.GetRequiredService<IFileLibraryService>();
await scanner.ScanDueRootsAsync(cancellationToken);
logger.LogInformation(
"文件库定时扫描轮询完成,耗时 {ElapsedMilliseconds} ms。",
(DateTime.UtcNow - startedAt).TotalMilliseconds);
}
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
{
logger.LogInformation("文件库定时扫描轮询已取消。");
}
catch (Exception ex)
{
@@ -0,0 +1,13 @@
namespace FileShare_API.Services
{
/// <summary>
/// 文件库定时扫描后台服务配置。
/// </summary>
public sealed class FileLibraryScanOptions
{
/// <summary>
/// 后台服务检查到期文件库目录的轮询间隔分钟数。
/// </summary>
public int PollingIntervalMinutes { get; set; } = 1;
}
}
+3
View File
@@ -29,5 +29,8 @@
"RootPath": "thumbnails",
"FfmpegPath": "tools/ffmpeg/bin/ffmpeg.exe",
"FfprobePath": "tools/ffmpeg/bin/ffprobe.exe"
},
"FileLibraryScanOptions": {
"PollingIntervalMinutes": 5
}
}