mirror of
https://github.com/DrizzleTime/Foxel.git
synced 2026-09-05 07:36:53 +08:00
refactor(picture): streamline picture response mapping and improve thumbnail generation logic
This commit is contained in:
@@ -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>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user