using DramaLing.Api.Models.Configuration; using Microsoft.Extensions.Options; namespace DramaLing.Api.Services.Storage; public class LocalImageStorageService : IImageStorageService { private readonly string _basePath; private readonly string _baseUrl; private readonly ILogger _logger; public LocalImageStorageService( IConfiguration configuration, ILogger logger) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _basePath = configuration["ImageStorage:Local:BasePath"] ?? "wwwroot/images/examples"; _baseUrl = configuration["ImageStorage:Local:BaseUrl"] ?? "https://localhost:5000/images/examples"; // 確保目錄存在 var fullPath = Path.GetFullPath(_basePath); if (!Directory.Exists(fullPath)) { Directory.CreateDirectory(fullPath); _logger.LogInformation("Created image storage directory: {Path}", fullPath); } } public async Task SaveImageAsync(Stream imageStream, string fileName) { try { var fullPath = Path.Combine(_basePath, fileName); var directory = Path.GetDirectoryName(fullPath); if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) { Directory.CreateDirectory(directory); } using var fileStream = new FileStream(fullPath, FileMode.Create); await imageStream.CopyToAsync(fileStream); _logger.LogInformation("Image saved to {Path}", fullPath); return fileName; // 回傳相對路徑 } catch (Exception ex) { _logger.LogError(ex, "Failed to save image {FileName}", fileName); throw; } } public Task GetImageUrlAsync(string imagePath) { var imageUrl = $"{_baseUrl.TrimEnd('/')}/{imagePath.TrimStart('/')}"; return Task.FromResult(imageUrl); } public Task DeleteImageAsync(string imagePath) { try { var fullPath = Path.Combine(_basePath, imagePath); if (File.Exists(fullPath)) { File.Delete(fullPath); _logger.LogInformation("Image deleted: {Path}", fullPath); return Task.FromResult(true); } return Task.FromResult(false); } catch (Exception ex) { _logger.LogError(ex, "Failed to delete image {ImagePath}", imagePath); return Task.FromResult(false); } } public Task ImageExistsAsync(string imagePath) { var fullPath = Path.Combine(_basePath, imagePath); return Task.FromResult(File.Exists(fullPath)); } public Task GetStorageInfoAsync() { try { var fullPath = Path.GetFullPath(_basePath); var directory = new DirectoryInfo(fullPath); if (!directory.Exists) { return Task.FromResult(new StorageInfo { Provider = "Local", Status = "Directory not found" }); } var files = directory.GetFiles("*", SearchOption.AllDirectories); var totalSize = files.Sum(f => f.Length); return Task.FromResult(new StorageInfo { Provider = "Local", TotalSizeBytes = totalSize, FileCount = files.Length, Status = "Available" }); } catch (Exception ex) { _logger.LogError(ex, "Failed to get storage info"); return Task.FromResult(new StorageInfo { Provider = "Local", Status = $"Error: {ex.Message}" }); } } }