Added simple support for image upload and requesting

This commit is contained in:
Mia Rose Winter 2024-01-13 15:30:34 +01:00
parent 4a4da6b8ee
commit f26f961e79
Signed by: miawinter
GPG key ID: 4B6F6A83178F595E
6 changed files with 133 additions and 2 deletions

View file

@ -0,0 +1,58 @@
@page "/upload"
@using Microsoft.AspNetCore.Authorization
@using Wave.Services
@using System.IO.Pipelines
@attribute [Authorize]
@rendermode InteractiveServer
@inject ImageService ImageService
<PageTitle>Upload</PageTitle>
<h3 class="title mb-3">Upload</h3>
@if (Busy) {
<progress class="progress progress-primary w-56"></progress>
}
<div>
<p>@Message</p>
@if (Path is not null) {
<img src="@Path" type="@ImageService.ImageMimeType" width="800" alt="" />
}
</div>
<InputFile OnChange="@LoadFiles" accept="image/png, image/jpeg, image/webp" />
@code {
private const long MaxFileSize = 1024 * 1024 * 5; // 5MB should be fine
private string Message { get; set; } = string.Empty;
private string? Path { get; set; }
private bool Busy { get; set; }
private async Task LoadFiles(InputFileChangeEventArgs args) {
Busy = true;
try {
string tempName = System.IO.Path.GetRandomFileName();
string tempDirectory = System.IO.Path.Combine(".", "files", "temp");
Directory.CreateDirectory(tempDirectory);
string tempPath = System.IO.Path.Combine(tempDirectory, tempName);
await using var fs = new FileStream(tempPath, FileMode.Create);
await args.File.OpenReadStream(MaxFileSize).CopyToAsync(fs);
var guid = await ImageService.StoreImageAsync(tempPath);
if (guid is null) {
Message = "Image upload failed";
} else {
Message = "Image uploaded successfully";
Path = "/images/" + guid;
}
} catch (Exception ex) {
Message = $"{ex.Message} ({ex.GetType().Name}).";
} finally {
Busy = false;
}
}
}

View file

@ -0,0 +1,20 @@
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using Wave.Services;
namespace Wave.Controllers;
[ApiController]
[Route("/images")]
public class ImageController(ImageService imageService) : ControllerBase {
private ImageService ImageService { get; } = imageService;
[HttpGet]
[Route("{imageId:guid}")]
public IActionResult Get(Guid imageId) {
string? path = ImageService.GetPath(imageId);
if (path is null) return NotFound();
return File(System.IO.File.OpenRead(path), ImageService.ImageMimeType);
}
}

View file

@ -3,6 +3,7 @@
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
USER app
WORKDIR /app
RUN mkdir ./files && chown app ./files
EXPOSE 8080
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
@ -21,5 +22,4 @@ RUN dotnet publish "./Wave.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:Us
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
RUN mkdir ./files
ENTRYPOINT ["dotnet", "Wave.dll"]

View file

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

View file

@ -10,6 +10,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Magick.NET-Q8-AnyCPU" Version="13.5.0" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0" />
@ -19,6 +20,8 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Controllers\" />
<Folder Include="Services\" />
<Folder Include="wwwroot\img\" />
</ItemGroup>

File diff suppressed because one or more lines are too long