Implemented a new background service, UploadsCleanupBackgroundService, to manage stale uploads in the uploads directory. Enhanced error handling in the media upload process to ensure incomplete uploads are deleted immediately upon failure, preventing unnecessary storage usage. Updated the .env.example and StorageOptions to include configuration for stale upload retention time. Improved documentation to reflect these changes and clarify the upload process.
60 lines
2.6 KiB
C#
60 lines
2.6 KiB
C#
using Microsoft.Extensions.Logging;
|
||
|
||
namespace TeleWave.Infrastructure.Media;
|
||
|
||
/// <summary>
|
||
/// Уборка перевалочного каталога <c>uploads/</c>: удаляет файлы, которых не касались дольше порога.
|
||
/// После регистрации ассета файл уходит в <c>originals/</c>, а токен загрузки нигде не хранится —
|
||
/// значит, всё залежавшееся в <c>uploads/</c> это след оборванной заливки или аварийного рестарта,
|
||
/// и подобрать его некому.
|
||
/// </summary>
|
||
public static class UploadsCleaner
|
||
{
|
||
/// <summary>
|
||
/// Один проход. Порог считается от времени последней записи, а не создания: идущая многочасовая
|
||
/// заливка правит файл и под него не попадает.
|
||
/// </summary>
|
||
public static void Sweep(MediaPathResolver paths, TimeSpan retention, ILogger logger)
|
||
{
|
||
try
|
||
{
|
||
if (!Directory.Exists(paths.UploadsDir))
|
||
return;
|
||
|
||
var deadline = DateTime.UtcNow - retention;
|
||
foreach (var path in Directory.EnumerateFiles(paths.UploadsDir))
|
||
DeleteIfStale(path, deadline, logger);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
logger.LogError(ex, "Ошибка уборки uploads/");
|
||
}
|
||
}
|
||
|
||
private static void DeleteIfStale(string path, DateTime deadline, ILogger logger)
|
||
{
|
||
try
|
||
{
|
||
var file = new FileInfo(path);
|
||
if (!file.Exists || file.LastWriteTimeUtc > deadline)
|
||
return;
|
||
|
||
// Метрики снимаем до удаления: после него FileInfo их уже не отдаст.
|
||
var size = file.Length;
|
||
var lastWrite = file.LastWriteTimeUtc;
|
||
file.Delete();
|
||
logger.LogInformation(
|
||
"Удалён брошенный файл загрузки {Name} ({Size} байт, последняя запись {LastWrite:u})",
|
||
file.Name,
|
||
size,
|
||
lastWrite
|
||
);
|
||
}
|
||
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException)
|
||
{
|
||
// Файл занят или права не те — попробуем на следующем тике.
|
||
logger.LogWarning(ex, "Не удалось удалить файл загрузки {Path}", path);
|
||
}
|
||
}
|
||
}
|