Files
Backend-Api/ProgramManager/src/Infrastructure/GozareshgirProgramManager.Infrastructure/Services/FileManagement/FileUploadService.cs
2026-01-27 16:32:55 +03:30

233 lines
8.0 KiB
C#

using GozareshgirProgramManager.Application.Services.FileManagement;
using GozareshgirProgramManager.Domain.FileManagementAgg.Entities;
using GozareshgirProgramManager.Domain.FileManagementAgg.Enums;
using GozareshgirProgramManager.Domain.FileManagementAgg.Repositories;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
namespace GozareshgirProgramManager.Infrastructure.Services.FileManagement;
/// <summary>
/// پیاده‌سازی سرویس آپلود کامل فایل
/// </summary>
public class FileUploadService : IFileUploadService
{
private readonly IUploadedFileRepository _fileRepository;
private readonly IFileStorageService _fileStorageService;
private readonly IThumbnailGeneratorService _thumbnailService;
private readonly ILogger<FileUploadService> _logger;
public FileUploadService(
IUploadedFileRepository fileRepository,
IFileStorageService fileStorageService,
IThumbnailGeneratorService thumbnailService,
ILogger<FileUploadService> logger)
{
_fileRepository = fileRepository;
_fileStorageService = fileStorageService;
_thumbnailService = thumbnailService;
_logger = logger;
}
public async Task<FileUploadResult> UploadFileAsync(
IFormFile file,
FileCategory category,
long uploadedByUserId,
long maxFileSizeBytes = 100 * 1024 * 1024)
{
try
{
// اعتبارسنجی ورودی
if (file.Length == 0)
{
return FileUploadResult.Failed("فایل خالی است");
}
if (file.Length > maxFileSizeBytes)
{
var maxSizeMb = maxFileSizeBytes / (1024 * 1024);
return FileUploadResult.Failed($"حجم فایل بیش از حد مجاز است (حداکثر {maxSizeMb}MB)");
}
using var stream = file.OpenReadStream();
return await UploadFileFromStreamAsync(
stream,
file.FileName,
file.ContentType,
category,
uploadedByUserId,
maxFileSizeBytes);
}
catch (Exception ex)
{
_logger.LogError(ex, "خطا در آپلود فایل {FileName}", file.FileName);
return FileUploadResult.Failed($"خطا در آپلود فایل: {ex.Message}");
}
}
public async Task<FileUploadResult> UploadFileFromStreamAsync(
Stream fileStream,
string fileName,
string contentType,
FileCategory category,
long uploadedByUserId,
long maxFileSizeBytes = 100 * 1024 * 1024)
{
UploadedFile? uploadedFile = null;
try
{
// تشخیص نوع فایل
var fileType = DetectFileType(contentType, Path.GetExtension(fileName));
// ایجاد رکورد فایل در دیتابیس
uploadedFile = new UploadedFile(
originalFileName: fileName,
fileSizeBytes: fileStream.Length,
mimeType: contentType,
fileType: fileType,
category: category,
uploadedByUserId: uploadedByUserId,
storageProvider: StorageProvider.LocalFileSystem
);
await _fileRepository.AddAsync(uploadedFile);
await _fileRepository.SaveChangesAsync();
// آپلود فایل به استوریج
var categoryFolder = category.ToString();
var uploadResult = await _fileStorageService.UploadAsync(
fileStream,
uploadedFile.UniqueFileName,
categoryFolder
);
// به‌روزرسانی اطلاعات آپلود
uploadedFile.CompleteUpload(uploadResult.StoragePath, uploadResult.StorageUrl);
// پردازش‌های خاص بر اساس نوع فایل
string? thumbnailUrl = null;
if (fileType == FileType.Image)
{
thumbnailUrl = await ProcessImageAsync(uploadedFile, uploadResult.StoragePath, categoryFolder);
}
else if (fileType == FileType.Video)
{
thumbnailUrl = await ProcessVideoAsync(uploadedFile, uploadResult.StoragePath, categoryFolder);
}
// ذخیره تغییرات نهایی
await _fileRepository.UpdateAsync(uploadedFile);
await _fileRepository.SaveChangesAsync();
_logger.LogInformation(
"فایل {FileName} با شناسه {FileId} با موفقیت آپلود شد",
fileName,
uploadedFile.Id);
return FileUploadResult.Success(uploadedFile.Id, uploadResult.StorageUrl, thumbnailUrl);
}
catch (Exception ex)
{
_logger.LogError(ex, "خطا در آپلود فایل {FileName}", fileName);
// در صورت خطا، فایل آپلود شده را حذف کنیم
if (uploadedFile != null)
{
try
{
await _fileRepository.DeleteAsync(uploadedFile);
await _fileRepository.SaveChangesAsync();
}
catch (Exception deleteEx)
{
_logger.LogError(deleteEx, "خطا در حذف فایل ناموفق {FileId}", uploadedFile.Id);
}
}
return FileUploadResult.Failed($"خطا در آپلود فایل: {ex.Message}");
}
}
/// <summary>
/// پردازش تصویر (ابعاد و thumbnail)
/// </summary>
private async Task<string?> ProcessImageAsync(UploadedFile file, string storagePath, string categoryFolder)
{
try
{
// دریافت ابعاد تصویر
var dimensions = await _thumbnailService.GetImageDimensionsAsync(storagePath);
if (dimensions.HasValue)
{
file.SetImageDimensions(dimensions.Value.Width, dimensions.Value.Height);
}
// تولید thumbnail
var thumbnail = await _thumbnailService.GenerateImageThumbnailAsync(
storagePath,
category: categoryFolder);
if (thumbnail.HasValue)
{
file.SetThumbnail(thumbnail.Value.ThumbnailUrl);
return thumbnail.Value.ThumbnailUrl;
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "خطا در پردازش تصویر {FileId}", file.Id);
}
return null;
}
/// <summary>
/// پردازش ویدیو (thumbnail)
/// </summary>
private async Task<string?> ProcessVideoAsync(UploadedFile file, string storagePath, string categoryFolder)
{
try
{
// تولید thumbnail از ویدیو
var thumbnail = await _thumbnailService.GenerateVideoThumbnailAsync(
storagePath,
category: categoryFolder);
if (thumbnail.HasValue)
{
file.SetThumbnail(thumbnail.Value.ThumbnailUrl);
return thumbnail.Value.ThumbnailUrl;
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "خطا در پردازش ویدیو {FileId}", file.Id);
}
return null;
}
/// <summary>
/// تشخیص نوع فایل از روی MIME type و extension
/// </summary>
private FileType DetectFileType(string mimeType, string extension)
{
if (mimeType.StartsWith("image/", StringComparison.OrdinalIgnoreCase))
return FileType.Image;
if (mimeType.StartsWith("video/", StringComparison.OrdinalIgnoreCase))
return FileType.Video;
if (mimeType.StartsWith("audio/", StringComparison.OrdinalIgnoreCase))
return FileType.Audio;
if (new[] { ".zip", ".rar", ".7z", ".tar", ".gz" }.Contains(extension.ToLower()))
return FileType.Archive;
return FileType.Document;
}
}