refactor(picture): streamline picture response mapping and improve thumbnail generation logic

This commit is contained in:
shiyu
2025-06-10 10:46:27 +08:00
parent 48730bebea
commit b5931de344
3 changed files with 42 additions and 63 deletions
@@ -1,5 +1,6 @@
using Foxel.Models; using Foxel.Models;
using Foxel.Models.Response.Picture; using Foxel.Models.Response.Picture;
using Foxel.Services.Mapping;
using Foxel.Services.Storage; using Foxel.Services.Storage;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@@ -8,6 +9,7 @@ namespace Foxel.Services.Management;
public class PictureManagementService( public class PictureManagementService(
IDbContextFactory<MyDbContext> contextFactory, IDbContextFactory<MyDbContext> contextFactory,
IStorageService storageService, IStorageService storageService,
IMappingService mappingService,
ILogger<PictureManagementService> logger) : IPictureManagementService ILogger<PictureManagementService> logger) : IPictureManagementService
{ {
public async Task<PaginatedResult<PictureResponse>> GetPicturesAsync(int page = 1, int pageSize = 10, string? searchQuery = null, int? userId = null) public async Task<PaginatedResult<PictureResponse>> GetPicturesAsync(int page = 1, int pageSize = 10, string? searchQuery = null, int? userId = null)
@@ -44,26 +46,7 @@ public class PictureManagementService(
.ToListAsync(); .ToListAsync();
// 转换为响应模型 // 转换为响应模型
var pictureResponses = pictures.Select(picture => new PictureResponse var pictureResponses = pictures.Select(mappingService.MapPictureToResponse).ToList();
{
Id = picture.Id,
Name = picture.Name,
Path = storageService.ExecuteAsync(picture.StorageModeId, provider =>
Task.FromResult(provider.GetUrl(picture.Id,picture.Path))).Result,
ThumbnailPath = storageService.ExecuteAsync(picture.StorageModeId, provider =>
Task.FromResult(provider.GetUrl(picture.Id,picture.ThumbnailPath ?? string.Empty))).Result,
Description = picture.Description,
CreatedAt = picture.CreatedAt,
TakenAt = picture.TakenAt,
ExifInfo = picture.ExifInfo,
UserId = picture.UserId,
Username = picture.User?.UserName,
Tags = picture.Tags?.Select(t => t.Name).ToList(),
AlbumId = picture.AlbumId,
AlbumName = picture.Album?.Name,
Permission = picture.Permission,
FavoriteCount = picture.Favorites?.Count ?? 0,
}).ToList();
return new PaginatedResult<PictureResponse> return new PaginatedResult<PictureResponse>
{ {
+3 -3
View File
@@ -17,12 +17,12 @@ namespace Foxel.Services.Mapping
{ {
coverPath = storageService.ExecuteAsync(album.CoverPicture.StorageModeId, coverPath = storageService.ExecuteAsync(album.CoverPicture.StorageModeId,
provider => Task.FromResult(provider.GetUrl(album.CoverPicture.Id, album.CoverPicture.Path))) provider => Task.FromResult(provider.GetUrl(album.CoverPicture.Id, album.CoverPicture.Path)))
.Result; // Consider making this async if possible in the future .Result;
if (!string.IsNullOrEmpty(album.CoverPicture.ThumbnailPath)) if (!string.IsNullOrEmpty(album.CoverPicture.ThumbnailPath))
{ {
coverThumbnailPath = storageService.ExecuteAsync(album.CoverPicture.StorageModeId, coverThumbnailPath = storageService.ExecuteAsync(album.CoverPicture.StorageModeId,
provider => Task.FromResult(provider.GetUrl(album.CoverPicture.Id, provider => Task.FromResult(provider.GetUrl(album.CoverPicture.Id,
album.CoverPicture.ThumbnailPath))).Result; // Consider async album.CoverPicture.ThumbnailPath))).Result;
} }
} }
@@ -50,7 +50,7 @@ namespace Foxel.Services.Mapping
Path = storageService.ExecuteAsync(picture.StorageModeId, provider => Path = storageService.ExecuteAsync(picture.StorageModeId, provider =>
Task.FromResult(provider.GetUrl(picture.Id, picture.Path))).Result, Task.FromResult(provider.GetUrl(picture.Id, picture.Path))).Result,
ThumbnailPath = storageService.ExecuteAsync(picture.StorageModeId, provider => ThumbnailPath = storageService.ExecuteAsync(picture.StorageModeId, provider =>
Task.FromResult(provider.GetUrl(picture.Id, picture.ThumbnailPath ?? string.Empty))) Task.FromResult(provider.GetUrl(picture.Id, picture.ThumbnailPath ?? picture.Path)))
.Result, .Result,
Description = picture.Description, Description = picture.Description,
CreatedAt = picture.CreatedAt, CreatedAt = picture.CreatedAt,
+36 -40
View File
@@ -520,51 +520,47 @@ public class PictureService(
var storedHdPath = await storageService.ExecuteAsync(storageModeId.Value, var storedHdPath = await storageService.ExecuteAsync(storageModeId.Value,
provider => provider.SaveAsync(convertedHdStream, hdStorageFileName!, hdContentType!)); provider => provider.SaveAsync(convertedHdStream, hdStorageFileName!, hdContentType!));
bool shouldGenerateThumbnailNow = userId.HasValue; try
if (shouldGenerateThumbnailNow)
{ {
try // 缩略图最大宽度
int thumbnailMaxWidth = 500; // 默认值
string thumbnailMaxWidthConfigKey = "Upload:ThumbnailMaxWidth";
string? thumbnailMaxWidthConfig = configuration[thumbnailMaxWidthConfigKey];
if (!string.IsNullOrEmpty(thumbnailMaxWidthConfig) && int.TryParse(thumbnailMaxWidthConfig, out int parsedMaxWidth))
{ {
// 缩略图最大宽度 thumbnailMaxWidth = Math.Max(100, parsedMaxWidth); // 最小宽度 100
int thumbnailMaxWidth = 500; // 默认值
string thumbnailMaxWidthConfigKey = "Upload:ThumbnailMaxWidth";
string? thumbnailMaxWidthConfig = configuration[thumbnailMaxWidthConfigKey];
if (!string.IsNullOrEmpty(thumbnailMaxWidthConfig) && int.TryParse(thumbnailMaxWidthConfig, out int parsedMaxWidth))
{
thumbnailMaxWidth = Math.Max(100, parsedMaxWidth); // 最小宽度 100
}
else
{
logger.LogWarning("配置项 '{ConfigKey}' 未找到或无效,使用默认缩略图最大宽度: {DefaultMaxWidth}", thumbnailMaxWidthConfigKey, thumbnailMaxWidth);
}
// 缩略图压缩质量
int thumbnailQuality = 75; // 默认值
string thumbnailQualityConfigKey = "Upload:ThumbnailCompressionQuality";
string? thumbnailQualityConfig = configuration[thumbnailQualityConfigKey];
if (!string.IsNullOrEmpty(thumbnailQualityConfig) && int.TryParse(thumbnailQualityConfig, out int parsedThumbQuality))
{
thumbnailQuality = Math.Clamp(parsedThumbQuality, 30, 90); // 限制在 30-90 之间
}
else
{
logger.LogWarning("配置项 '{ConfigKey}' 未找到或无效,使用默认缩略图压缩质量: {DefaultThumbQuality}", thumbnailQualityConfigKey, thumbnailQuality);
}
tempThumbnailLocalPath = Path.GetTempFileName() + ".webp";
File.Move(Path.GetTempFileName(), tempThumbnailLocalPath);
await ImageHelper.CreateThumbnailAsync(tempOriginalLocalPath, tempThumbnailLocalPath, thumbnailMaxWidth, thumbnailQuality);
string thumbnailUploadFileName = $"{baseName}-thumbnail.webp";
await using var thumbnailFileStream =
new FileStream(tempThumbnailLocalPath!, FileMode.Open, FileAccess.Read);
storedThumbnailPath = await storageService.ExecuteAsync(storageModeId.Value,
provider => provider.SaveAsync(thumbnailFileStream, thumbnailUploadFileName!, "image/webp"));
} }
catch (Exception ex) else
{ {
logger.LogError(ex, "生成和上传缩略图失败 during initial upload"); logger.LogWarning("配置项 '{ConfigKey}' 未找到或无效,使用默认缩略图最大宽度: {DefaultMaxWidth}", thumbnailMaxWidthConfigKey, thumbnailMaxWidth);
} }
// 缩略图压缩质量
int thumbnailQuality = 75; // 默认值
string thumbnailQualityConfigKey = "Upload:ThumbnailCompressionQuality";
string? thumbnailQualityConfig = configuration[thumbnailQualityConfigKey];
if (!string.IsNullOrEmpty(thumbnailQualityConfig) && int.TryParse(thumbnailQualityConfig, out int parsedThumbQuality))
{
thumbnailQuality = Math.Clamp(parsedThumbQuality, 30, 90); // 限制在 30-90 之间
}
else
{
logger.LogWarning("配置项 '{ConfigKey}' 未找到或无效,使用默认缩略图压缩质量: {DefaultThumbQuality}", thumbnailQualityConfigKey, thumbnailQuality);
}
tempThumbnailLocalPath = Path.GetTempFileName() + ".webp";
File.Move(Path.GetTempFileName(), tempThumbnailLocalPath);
await ImageHelper.CreateThumbnailAsync(tempOriginalLocalPath, tempThumbnailLocalPath, thumbnailMaxWidth, thumbnailQuality);
string thumbnailUploadFileName = $"{baseName}-thumbnail.webp";
await using var thumbnailFileStream =
new FileStream(tempThumbnailLocalPath!, FileMode.Open, FileAccess.Read);
storedThumbnailPath = await storageService.ExecuteAsync(storageModeId.Value,
provider => provider.SaveAsync(thumbnailFileStream, thumbnailUploadFileName!, "image/webp"));
}
catch (Exception ex)
{
logger.LogError(ex, "生成和上传缩略图失败 during initial upload");
} }
string initialTitle = Path.GetFileNameWithoutExtension(fileName); string initialTitle = Path.GetFileNameWithoutExtension(fileName);