From 6c5bc3685e0df69e58b68293863675dbfaf1a7f8 Mon Sep 17 00:00:00 2001 From: Glax Date: Fri, 7 Mar 2025 14:29:25 +0100 Subject: [PATCH] #229 Resize cover-images on request before transfer --- API/Controllers/MangaController.cs | 23 +++++++++++++++++++++-- API/CoverFormatRequestRecord.cs | 14 ++++++++++++++ API/Program.cs | 6 +++++- 3 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 API/CoverFormatRequestRecord.cs diff --git a/API/Controllers/MangaController.cs b/API/Controllers/MangaController.cs index e05d155..9f838c5 100644 --- a/API/Controllers/MangaController.cs +++ b/API/Controllers/MangaController.cs @@ -1,8 +1,11 @@ using API.Schema; using Asp.Versioning; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ModelBinding; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Transforms; using static Microsoft.AspNetCore.Http.StatusCodes; namespace API.Controllers; @@ -88,14 +91,18 @@ public class MangaController(PgsqlContext context) : Controller /// Returns Cover of Manga /// /// Manga-ID + /// Formatting/Resizing Request /// JPEG Image /// Cover not loaded + /// The formatting-request was invalid /// Manga with ID not found - [HttpGet("{id}/Cover")] + [HttpPost("{id}/Cover")] + [Produces("image/jpeg")] [ProducesResponseType(Status200OK)] [ProducesResponseType(Status204NoContent)] + [ProducesResponseType(Status400BadRequest)] [ProducesResponseType(Status404NotFound)] - public IActionResult GetCover(string id) + public IActionResult GetCover(string id, [FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)]CoverFormatRequestRecord? formatRequest) { Manga? m = context.Manga.Find(id); if (m is null) @@ -104,6 +111,18 @@ public class MangaController(PgsqlContext context) : Controller return NoContent(); Image image = Image.Load(m.CoverFileNameInCache); + + if (formatRequest is not null) + { + if(!formatRequest.Validate()) + return BadRequest(); + image.Mutate(i => i.ApplyProcessor(new ResizeProcessor(new ResizeOptions() + { + Mode = ResizeMode.Max, + Size = formatRequest.size + }, image.Size))); + } + using MemoryStream ms = new(); image.Save(ms, new JpegEncoder(){Quality = 100}); return File(ms.GetBuffer(), "image/jpeg"); diff --git a/API/CoverFormatRequestRecord.cs b/API/CoverFormatRequestRecord.cs new file mode 100644 index 0000000..429761a --- /dev/null +++ b/API/CoverFormatRequestRecord.cs @@ -0,0 +1,14 @@ +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats; + +namespace API; + +public record CoverFormatRequestRecord(Size size) +{ + public bool Validate() + { + if (size.Height <= 0 || size.Width <= 0 || size.Height > 65535 || size.Width > 65535) //JPEG max size + return false; + return true; + } +} \ No newline at end of file diff --git a/API/Program.cs b/API/Program.cs index fe444af..a1c9635 100644 --- a/API/Program.cs +++ b/API/Program.cs @@ -57,7 +57,11 @@ builder.Services.AddDbContext(options => $"Username={Environment.GetEnvironmentVariable("POSTGRES_USER")??"postgres"}; " + $"Password={Environment.GetEnvironmentVariable("POSTGRES_PASSWORD")??"postgres"}")); -builder.Services.AddControllers().AddNewtonsoftJson(opts => +builder.Services.AddControllers(options => + { + options.AllowEmptyInputInBodyModelBinding = true; + }) + .AddNewtonsoftJson(opts => { opts.SerializerSettings.Converters.Add(new StringEnumConverter()); });