diff --git a/API/Controllers/ImageController.cs b/API/Controllers/ImageController.cs new file mode 100644 index 000000000..c7812ac63 --- /dev/null +++ b/API/Controllers/ImageController.cs @@ -0,0 +1,79 @@ +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using API.DTOs; +using API.Interfaces; +using API.Interfaces.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace API.Controllers +{ + public class ImageController : BaseApiController + { + private readonly IDirectoryService _directoryService; + private readonly ICacheService _cacheService; + private readonly ILogger _logger; + private readonly IUnitOfWork _unitOfWork; + + public ImageController(IDirectoryService directoryService, ICacheService cacheService, + ILogger logger, IUnitOfWork unitOfWork) + { + _directoryService = directoryService; + _cacheService = cacheService; + _logger = logger; + _unitOfWork = unitOfWork; + } + + [HttpGet("chapter-cover")] + public async Task GetChapterCoverImage(int chapterId) + { + // TODO: Write custom methods to just get the byte[] as fast as possible + var chapter = await _unitOfWork.VolumeRepository.GetChapterAsync(chapterId); + var content = chapter.CoverImage; + var format = "jpeg"; //Path.GetExtension("jpeg").Replace(".", ""); + + // Calculates SHA1 Hash for byte[] + using var sha1 = new System.Security.Cryptography.SHA1CryptoServiceProvider(); + Response.Headers.Add("ETag", string.Concat(sha1.ComputeHash(content).Select(x => x.ToString("X2")))); + Response.Headers.Add("Cache-Control", "private"); + + return File(content, "image/" + format); + } + + [HttpGet("volume-cover")] + public async Task GetVolumeCoverImage(int volumeId) + { + var volume = await _unitOfWork.SeriesRepository.GetVolumeAsync(volumeId); + var content = volume.CoverImage; + var format = "jpeg"; //Path.GetExtension("jpeg").Replace(".", ""); + + // Calculates SHA1 Hash for byte[] + using var sha1 = new System.Security.Cryptography.SHA1CryptoServiceProvider(); + Response.Headers.Add("ETag", string.Concat(sha1.ComputeHash(content).Select(x => x.ToString("X2")))); + Response.Headers.Add("Cache-Control", "private"); + + return File(content, "image/" + format); + } + + [HttpGet("series-cover")] + public async Task GetSeriesCoverImage(int seriesId) + { + var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId); + var content = series.CoverImage; + var format = "jpeg"; //Path.GetExtension("jpeg").Replace(".", ""); + + if (content.Length == 0) + { + // How do I handle? + } + + // Calculates SHA1 Hash for byte[] + using var sha1 = new System.Security.Cryptography.SHA1CryptoServiceProvider(); + Response.Headers.Add("ETag", string.Concat(sha1.ComputeHash(content).Select(x => x.ToString("X2")))); + Response.Headers.Add("Cache-Control", "private"); + + return File(content, "image/" + format); + } + } +} \ No newline at end of file diff --git a/API/DTOs/ChapterDto.cs b/API/DTOs/ChapterDto.cs index ee58e6c18..bfff08ef9 100644 --- a/API/DTOs/ChapterDto.cs +++ b/API/DTOs/ChapterDto.cs @@ -13,7 +13,7 @@ namespace API.DTOs /// Smallest number of the Range. /// public string Number { get; set; } - public byte[] CoverImage { get; set; } + //public byte[] CoverImage { get; set; } /// /// Total number of pages in all MangaFiles /// diff --git a/API/DTOs/MangaFileDto.cs b/API/DTOs/MangaFileDto.cs index 26bed91b8..03f3e9abf 100644 --- a/API/DTOs/MangaFileDto.cs +++ b/API/DTOs/MangaFileDto.cs @@ -5,7 +5,7 @@ namespace API.DTOs public class MangaFileDto { public string FilePath { get; set; } - public int NumberOfPages { get; set; } + public int NumberOfPages { get; set; } // TODO: Refactor to Pages public MangaFormat Format { get; set; } } diff --git a/API/DTOs/VolumeDto.cs b/API/DTOs/VolumeDto.cs index e9e08ec72..95df511ce 100644 --- a/API/DTOs/VolumeDto.cs +++ b/API/DTOs/VolumeDto.cs @@ -9,7 +9,7 @@ namespace API.DTOs public int Id { get; set; } public int Number { get; set; } public string Name { get; set; } - public byte[] CoverImage { get; set; } + //public byte[] CoverImage { get; set; } public int Pages { get; set; } public int PagesRead { get; set; } public DateTime LastModified { get; set; } diff --git a/API/Data/SeriesRepository.cs b/API/Data/SeriesRepository.cs index 5e0b55d29..1af322802 100644 --- a/API/Data/SeriesRepository.cs +++ b/API/Data/SeriesRepository.cs @@ -143,6 +143,16 @@ namespace API.Data .SingleOrDefaultAsync(vol => vol.Id == volumeId); } + public async Task GetVolumeDtoAsync(int volumeId) + { + return await _context.Volume + .Where(vol => vol.Id == volumeId) + .AsNoTracking() + .ProjectTo(_mapper.ConfigurationProvider) + .SingleAsync(); + + } + public async Task GetVolumeDtoAsync(int volumeId, int userId) { var volume = await _context.Volume diff --git a/API/Interfaces/ISeriesRepository.cs b/API/Interfaces/ISeriesRepository.cs index 6a9845975..e8b4f1aee 100644 --- a/API/Interfaces/ISeriesRepository.cs +++ b/API/Interfaces/ISeriesRepository.cs @@ -34,6 +34,12 @@ namespace API.Interfaces Task GetSeriesDtoByIdAsync(int seriesId, int userId); Task GetVolumeAsync(int volumeId); Task GetVolumeDtoAsync(int volumeId, int userId); + /// + /// A fast lookup of just the volume information with no tracking. + /// + /// + /// + Task GetVolumeDtoAsync(int volumeId); Task> GetVolumesForSeriesAsync(int[] seriesIds); Task DeleteSeriesAsync(int seriesId); Task GetVolumeByIdAsync(int volumeId);