using Microsoft.AspNetCore.Hosting; using NexusReader.Application.Abstractions.Services; namespace NexusReader.Infrastructure.Services; /// /// Infrastructure implementation of book storage using local filesystem. /// All paths returned are relative to the web root. /// public class BookStorageService : IBookStorageService { private readonly IWebHostEnvironment _environment; public BookStorageService(IWebHostEnvironment environment) { _environment = environment; } public async Task SaveEbookAsync(byte[] data, string fileName) { using var stream = new MemoryStream(data); return await SaveEbookAsync(stream, fileName); } public async Task SaveEbookAsync(Stream data, string fileName) { var uploadsFolder = Path.Combine(_environment.WebRootPath, "uploads"); EnsureDirectoryExists(uploadsFolder); var uniqueFileName = $"{Guid.NewGuid()}_{fileName}"; var filePath = Path.Combine(uploadsFolder, uniqueFileName); using (var fileStream = new FileStream(filePath, FileMode.Create)) { await data.CopyToAsync(fileStream); } return Path.Combine("uploads", uniqueFileName); } public async Task SaveCoverAsync(byte[] data, string fileName) { if (data == null || data.Length == 0) return null; using var stream = new MemoryStream(data); return await SaveCoverAsync(stream, fileName); } public async Task SaveCoverAsync(Stream data, string fileName) { var coversFolder = Path.Combine(_environment.WebRootPath, "covers"); EnsureDirectoryExists(coversFolder); var uniqueFileName = $"{Guid.NewGuid()}_{fileName}"; var filePath = Path.Combine(coversFolder, uniqueFileName); using (var fileStream = new FileStream(filePath, FileMode.Create)) { await data.CopyToAsync(fileStream); } return Path.Combine("covers", uniqueFileName); } private void EnsureDirectoryExists(string path) { if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } } }