Improved image endpoints, added size to ImageController

This commit is contained in:
Mia Rose Winter 2024-02-09 12:01:01 +01:00
parent 0491678055
commit 21afbf5a39
Signed by: miawinter
GPG key ID: 4B6F6A83178F595E
3 changed files with 59 additions and 60 deletions

View file

@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using System.ComponentModel.DataAnnotations;
using Wave.Services; using Wave.Services;
namespace Wave.Controllers; namespace Wave.Controllers;
@ -7,14 +8,15 @@ namespace Wave.Controllers;
[ApiController] [ApiController]
[Route("/images")] [Route("/images")]
public class ImageController(ImageService imageService) : ControllerBase { public class ImageController(ImageService imageService) : ControllerBase {
private ImageService ImageService { get; } = imageService; private ImageService ImageService { get; } = imageService;
[HttpGet] [HttpGet]
[Route("{imageId:guid}")] [Route("{imageId:guid}")]
public IActionResult Get(Guid imageId) { public async Task<IActionResult> Get(Guid imageId, [FromQuery, Range(16, 800)] int size = 800) {
string? path = ImageService.GetPath(imageId); string? path = ImageService.GetPath(imageId);
if (path is null) return NotFound(); if (path is null) return NotFound();
return File(System.IO.File.OpenRead(path), ImageService.ImageMimeType); if (size < 800) return File(await ImageService.GetResized(path, size), ImageService.ImageMimeType);
} return File(System.IO.File.OpenRead(path), ImageService.ImageMimeType);
}
} }

View file

@ -1,5 +1,4 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using ImageMagick;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.OutputCaching; using Microsoft.AspNetCore.OutputCaching;
@ -31,15 +30,7 @@ public class UserController(ImageService imageService, IDbContextFactory<Applica
string? path = ImageService.GetPath(user.ProfilePicture.ImageId); string? path = ImageService.GetPath(user.ProfilePicture.ImageId);
if (path is null) return NotFound(); if (path is null) return NotFound();
if (size < 800) { if (size < 800) return File(await ImageService.GetResized(path, size), ImageService.ImageMimeType);
var image = new MagickImage(path);
image.Resize(new MagickGeometry(size));
using var memory = new MemoryStream();
await image.WriteAsync(memory);
return File(memory.GetBuffer(), ImageService.ImageMimeType);
}
return File(System.IO.File.OpenRead(path), ImageService.ImageMimeType); return File(System.IO.File.OpenRead(path), ImageService.ImageMimeType);
} }

View file

@ -1,56 +1,62 @@
using ImageMagick; using ImageMagick;
using static System.Net.Mime.MediaTypeNames;
namespace Wave.Services; namespace Wave.Services;
public class ImageService(ILogger<ImageService> logger) { public class ImageService(ILogger<ImageService> logger) {
private ILogger<ImageService> Logger { get; } = logger; private ILogger<ImageService> Logger { get; } = logger;
private const string BasePath = "./files/images"; private const string BasePath = "./files/images";
private const string ImageExtension = ".jpg"; private const string ImageExtension = ".jpg";
public string ImageMimeType => "image/jpg"; public string ImageMimeType => "image/jpg";
public string? GetPath(Guid imageId) { public string? GetPath(Guid imageId) {
string path = Path.Combine(BasePath, imageId + ImageExtension); string path = Path.Combine(BasePath, imageId + ImageExtension);
return File.Exists(path) ? path : null; return File.Exists(path) ? path : null;
} }
public async ValueTask<Guid?> StoreImageAsync(string temporaryPath, int size = 800, CancellationToken cancellation = default) { public async Task<byte[]> GetResized(string path, int size) {
if (File.Exists(temporaryPath) is not true) return null; var image = new MagickImage(path);
image.Resize(new MagickGeometry(size));
using var memory = new MemoryStream();
await image.WriteAsync(memory);
return memory.ToArray();
}
try { public async ValueTask<Guid?> StoreImageAsync(string temporaryPath, int size = 800,
if (File.Exists(BasePath) is not true) Directory.CreateDirectory(BasePath); CancellationToken cancellation = default) {
if (File.Exists(temporaryPath) is not true) return null;
var image = new MagickImage(); try {
await image.ReadAsync(temporaryPath, cancellation); if (File.Exists(BasePath) is not true) Directory.CreateDirectory(BasePath);
// Jpeg with 90% compression should look decent var image = new MagickImage();
image.Resize(new MagickGeometry(size)); // this preserves aspect ratio await image.ReadAsync(temporaryPath, cancellation);
image.Format = MagickFormat.Jpeg;
image.Quality = 90;
if (image.GetExifProfile() is not null) { // Jpeg with 90% compression should look decent
image.RemoveProfile(image.GetExifProfile()); image.Resize(new MagickGeometry(size)); // this preserves aspect ratio
} image.Format = MagickFormat.Jpeg;
image.Quality = 90;
// Overwrite exif for privacy reasons if (image.GetExifProfile() is { } exifProfile) image.RemoveProfile(exifProfile);
var exif = new ExifProfile {
Parts = ExifParts.None
};
exif.CreateThumbnail();
image.SetProfile(exif);
var guid = Guid.NewGuid(); // Overwrite exif for privacy reasons
string path = Path.Combine(BasePath, guid + ImageExtension); var exif = new ExifProfile {
await image.WriteAsync(path, cancellation); Parts = ExifParts.None
return guid; };
} catch (Exception ex) { exif.CreateThumbnail();
Logger.LogInformation(ex, "Failed to process uploaded image."); image.SetProfile(exif);
return null;
}
}
public void Delete(Guid imageId) { var guid = Guid.NewGuid();
string path = Path.Combine(BasePath, imageId + ImageExtension); string path = Path.Combine(BasePath, guid + ImageExtension);
File.Delete(path); await image.WriteAsync(path, cancellation);
} return guid;
} catch (Exception ex) {
Logger.LogInformation(ex, "Failed to process uploaded image.");
return null;
}
}
public void Delete(Guid imageId) {
string path = Path.Combine(BasePath, imageId + ImageExtension);
File.Delete(path);
}
} }