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; /// /// پیاده‌سازی سرویس آپلود کامل فایل /// public class FileUploadService : IFileUploadService { private readonly IUploadedFileRepository _fileRepository; private readonly IFileStorageService _fileStorageService; private readonly IThumbnailGeneratorService _thumbnailService; private readonly ILogger _logger; public FileUploadService( IUploadedFileRepository fileRepository, IFileStorageService fileStorageService, IThumbnailGeneratorService thumbnailService, ILogger logger) { _fileRepository = fileRepository; _fileStorageService = fileStorageService; _thumbnailService = thumbnailService; _logger = logger; } public async Task 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 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}"); } } /// /// پردازش تصویر (ابعاد و thumbnail) /// private async Task 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; } /// /// پردازش ویدیو (thumbnail) /// private async Task 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; } /// /// تشخیص نوع فایل از روی MIME type و extension /// 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; } }