feat(StorageService, PictureController): refactor storage service methods to use unified ExecuteAsync pattern for file operations

This commit is contained in:
shiyu
2025-05-28 18:47:57 +08:00
parent 672e0611fa
commit f52507b129
5 changed files with 43 additions and 76 deletions
+5 -4
View File
@@ -6,7 +6,6 @@ using Foxel.Models.Request.Picture;
using Foxel.Models.Response.Picture; using Foxel.Models.Response.Picture;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Foxel.Services.Configuration;
using Foxel.Services.Media; using Foxel.Services.Media;
using Foxel.Services.Storage; using Foxel.Services.Storage;
using System.IO; using System.IO;
@@ -16,7 +15,7 @@ namespace Foxel.Controllers;
[Authorize] [Authorize]
[Route("api/picture")] [Route("api/picture")]
public class PictureController(IPictureService pictureService, IConfigService configService, IStorageService storageService) : BaseApiController public class PictureController(IPictureService pictureService, IStorageService storageService) : BaseApiController
{ {
[HttpGet("get_pictures")] [HttpGet("get_pictures")]
public async Task<ActionResult<PaginatedResult<PictureResponse>>> GetPictures( public async Task<ActionResult<PaginatedResult<PictureResponse>>> GetPictures(
@@ -280,7 +279,8 @@ public class PictureController(IPictureService pictureService, IConfigService co
try try
{ {
// 使用 storageService 下载文件,这样会自动使用配置的代理 // 使用 storageService 下载文件,这样会自动使用配置的代理
string tempFilePath = await storageService.DownloadFileAsync(StorageType.Telegram, storagePath); string tempFilePath = await storageService.ExecuteAsync(StorageType.Telegram,
provider => provider.DownloadFileAsync(storagePath));
// 获取文件内容类型 // 获取文件内容类型
string contentType = GetContentTypeFromPath(tempFilePath); string contentType = GetContentTypeFromPath(tempFilePath);
@@ -327,7 +327,8 @@ public class PictureController(IPictureService pictureService, IConfigService co
} }
// 下载文件到临时位置 // 下载文件到临时位置
string filePath = await storageService.DownloadFileAsync(StorageType.WebDAV,path); string filePath = await storageService.ExecuteAsync(StorageType.WebDAV,
provider => provider.DownloadFileAsync(path));
// 确定内容类型 // 确定内容类型
string contentType = GetContentTypeFromPath(path); string contentType = GetContentTypeFromPath(path);
+4 -5
View File
@@ -225,7 +225,8 @@ public sealed class BackgroundTaskQueue : IBackgroundTaskQueue, IDisposable
{ {
// 非本地存储需要先下载文件 // 非本地存储需要先下载文件
await UpdatePictureStatus(task.PictureId, ProcessingStatus.Processing, 15); await UpdatePictureStatus(task.PictureId, ProcessingStatus.Processing, 15);
localFilePath = await storageService.DownloadFileAsync(picture.StorageType, picture.Path); localFilePath = await storageService.ExecuteAsync(picture.StorageType,
provider => provider.DownloadFileAsync(picture.Path));
isTempFile = true; isTempFile = true;
} }
@@ -259,11 +260,9 @@ public sealed class BackgroundTaskQueue : IBackgroundTaskQueue, IDisposable
var thumbnailContentType = "image/webp"; var thumbnailContentType = "image/webp";
// 上传缩略图并获取存储路径或元数据 // 上传缩略图并获取存储路径或元数据
string thumbnailStoragePath = await storageService.SaveAsync( string thumbnailStoragePath = await storageService.ExecuteAsync(
picture.StorageType, picture.StorageType,
thumbnailFileStream, provider => provider.SaveAsync(thumbnailFileStream, thumbnailFileName, thumbnailContentType));
thumbnailFileName,
thumbnailContentType);
// 将路径或元数据存储到ThumbnailPath // 将路径或元数据存储到ThumbnailPath
picture.ThumbnailPath = thumbnailStoragePath; picture.ThumbnailPath = thumbnailStoragePath;
+19 -10
View File
@@ -377,8 +377,10 @@ public class PictureService(
{ {
Id = picture.Id, Id = picture.Id,
Name = picture.Name, Name = picture.Name,
Path = storageService.GetUrl(picture.StorageType, picture.Path), Path = storageService.ExecuteAsync(picture.StorageType, provider =>
ThumbnailPath = storageService.GetUrl(picture.StorageType, picture.ThumbnailPath), Task.FromResult(provider.GetUrl(picture.Path ?? string.Empty))).Result,
ThumbnailPath = storageService.ExecuteAsync(picture.StorageType, provider =>
Task.FromResult(provider.GetUrl(picture.ThumbnailPath ?? string.Empty))).Result,
Description = picture.Description, Description = picture.Description,
CreatedAt = picture.CreatedAt, CreatedAt = picture.CreatedAt,
Tags = picture.Tags != null ? picture.Tags.Select(t => t.Name).ToList() : new List<string>(), Tags = picture.Tags != null ? picture.Tags.Select(t => t.Name).ToList() : new List<string>(),
@@ -551,7 +553,8 @@ public class PictureService(
try try
{ {
// 使用存储服务保存文件 // 使用存储服务保存文件
string relativePath = await storageService.SaveAsync(storageType.Value, finalStream, finalFileName, finalContentType); string relativePath = await storageService.ExecuteAsync(storageType.Value,
provider => provider.SaveAsync(finalStream, finalFileName, finalContentType));
// 创建基本的Picture对象,使用文件名作为标题和描述 // 创建基本的Picture对象,使用文件名作为标题和描述
string initialTitle = Path.GetFileNameWithoutExtension(originalFileName); string initialTitle = Path.GetFileNameWithoutExtension(originalFileName);
@@ -617,8 +620,10 @@ public class PictureService(
{ {
Id = picture.Id, Id = picture.Id,
Name = picture.Name, Name = picture.Name,
Path = storageService.GetUrl(picture.StorageType, relativePath), Path = await storageService.ExecuteAsync(picture.StorageType, provider =>
ThumbnailPath = isAnonymous ? storageService.GetUrl(picture.StorageType, relativePath) : null, Task.FromResult(provider.GetUrl(relativePath))),
ThumbnailPath = isAnonymous ? await storageService.ExecuteAsync(picture.StorageType, provider =>
Task.FromResult(provider.GetUrl(relativePath))) : null,
Description = picture.Description, Description = picture.Description,
CreatedAt = picture.CreatedAt, CreatedAt = picture.CreatedAt,
Tags = new List<string>(), Tags = new List<string>(),
@@ -695,7 +700,7 @@ public class PictureService(
new List<(int PictureId, string Path, string ThumbnailPath, int? UserId, StorageType StorageType)>(); new List<(int PictureId, string Path, string ThumbnailPath, int? UserId, StorageType StorageType)>();
foreach (var picture in picturesToDelete) foreach (var picture in picturesToDelete)
{ {
filesToDelete.Add((picture.Id, picture.Path, picture.ThumbnailPath, picture.User?.Id, picture.StorageType)); filesToDelete.Add((picture.Id, picture.Path, picture.ThumbnailPath ?? string.Empty, picture.User?.Id, picture.StorageType));
} }
if (picturesToDelete.Any()) if (picturesToDelete.Any())
@@ -713,12 +718,14 @@ public class PictureService(
try try
{ {
// 使用存储服务删除文件 // 使用存储服务删除文件
await storageService.DeleteAsync(storageType, path); await storageService.ExecuteAsync(storageType,
provider => provider.DeleteAsync(path));
// 删除缩略图 // 删除缩略图
if (!string.IsNullOrEmpty(thumbnailPath)) if (!string.IsNullOrEmpty(thumbnailPath))
{ {
await storageService.DeleteAsync(storageType, thumbnailPath); await storageService.ExecuteAsync(storageType,
provider => provider.DeleteAsync(thumbnailPath));
} }
} }
catch (Exception ex) catch (Exception ex)
@@ -819,8 +826,10 @@ public class PictureService(
{ {
Id = picture.Id, Id = picture.Id,
Name = picture.Name, Name = picture.Name,
Path = storageService.GetUrl(picture.StorageType, picture.Path), Path = await storageService.ExecuteAsync(picture.StorageType, provider =>
ThumbnailPath = storageService.GetUrl(picture.StorageType, picture.ThumbnailPath), Task.FromResult(provider.GetUrl(picture.Path ?? string.Empty))),
ThumbnailPath = await storageService.ExecuteAsync(picture.StorageType, provider =>
Task.FromResult(provider.GetUrl(picture.ThumbnailPath ?? string.Empty))),
Description = picture.Description, Description = picture.Description,
CreatedAt = picture.CreatedAt, CreatedAt = picture.CreatedAt,
Tags = picture.Tags?.Select(t => t.Name).ToList() ?? new List<string>(), Tags = picture.Tags?.Select(t => t.Name).ToList() ?? new List<string>(),
+8 -32
View File
@@ -8,42 +8,18 @@ namespace Foxel.Services.Storage;
public interface IStorageService public interface IStorageService
{ {
/// <summary> /// <summary>
/// 根据存储类型获取对应的存储提供者 /// 在指定存储类型上执行操作
/// </summary> /// </summary>
/// <typeparam name="TResult">操作结果类型</typeparam>
/// <param name="storageType">存储类型</param> /// <param name="storageType">存储类型</param>
/// <returns>存储提供者实例</returns> /// <param name="operation">要执行的操作</param>
IStorageProvider GetProvider(StorageType storageType); /// <returns>操作结果</returns>
Task<TResult> ExecuteAsync<TResult>(StorageType storageType, Func<IStorageProvider, Task<TResult>> operation);
/// <summary> /// <summary>
/// 使用指定存储类型保存文件 /// 指定存储类型上执行无返回值的操作
/// </summary> /// </summary>
/// <param name="storageType">存储类型</param> /// <param name="storageType">存储类型</param>
/// <param name="fileStream">文件流</param> /// <param name="operation">要执行的操作</param>
/// <param name="fileName">文件名</param> Task ExecuteAsync(StorageType storageType, Func<IStorageProvider, Task> operation);
/// <param name="contentType">内容类型</param>
/// <returns>存储路径</returns>
Task<string> SaveAsync(StorageType storageType, Stream fileStream, string fileName, string contentType);
/// <summary>
/// 使用指定存储类型删除文件
/// </summary>
/// <param name="storageType">存储类型</param>
/// <param name="storagePath">存储路径</param>
Task DeleteAsync(StorageType storageType, string storagePath);
/// <summary>
/// 使用指定存储类型获取文件URL
/// </summary>
/// <param name="storageType">存储类型</param>
/// <param name="storagePath">存储路径</param>
/// <returns>文件URL</returns>
string GetUrl(StorageType storageType, string storagePath);
/// <summary>
/// 使用指定存储类型下载文件
/// </summary>
/// <param name="storageType">存储类型</param>
/// <param name="storagePath">存储路径</param>
/// <returns>本地文件路径</returns>
Task<string> DownloadFileAsync(StorageType storageType, string storagePath);
} }
+7 -25
View File
@@ -56,7 +56,7 @@ public class StorageService : IStorageService
/// <summary> /// <summary>
/// 获取指定存储类型的提供者实例 /// 获取指定存储类型的提供者实例
/// </summary> /// </summary>
public IStorageProvider GetProvider(StorageType storageType) private IStorageProvider GetProvider(StorageType storageType)
{ {
if (!_storageProviders.TryGetValue(storageType, out var providerType)) if (!_storageProviders.TryGetValue(storageType, out var providerType))
{ {
@@ -67,38 +67,20 @@ public class StorageService : IStorageService
} }
/// <summary> /// <summary>
/// 使用指定存储类型保存文件 /// 指定存储类型上执行操作
/// </summary> /// </summary>
public Task<string> SaveAsync(StorageType storageType, Stream fileStream, string fileName, string contentType) public async Task<TResult> ExecuteAsync<TResult>(StorageType storageType, Func<IStorageProvider, Task<TResult>> operation)
{ {
var provider = GetProvider(storageType); var provider = GetProvider(storageType);
return provider.SaveAsync(fileStream, fileName, contentType); return await operation(provider);
} }
/// <summary> /// <summary>
/// 使用指定存储类型删除文件 /// 指定存储类型上执行无返回值的操作
/// </summary> /// </summary>
public Task DeleteAsync(StorageType storageType, string storagePath) public async Task ExecuteAsync(StorageType storageType, Func<IStorageProvider, Task> operation)
{ {
var provider = GetProvider(storageType); var provider = GetProvider(storageType);
return provider.DeleteAsync(storagePath); await operation(provider);
}
/// <summary>
/// 使用指定存储类型获取文件URL
/// </summary>
public string GetUrl(StorageType storageType, string storagePath)
{
var provider = GetProvider(storageType);
return provider.GetUrl(storagePath);
}
/// <summary>
/// 使用指定存储类型下载文件
/// </summary>
public Task<string> DownloadFileAsync(StorageType storageType, string storagePath)
{
var provider = GetProvider(storageType);
return provider.DownloadFileAsync(storagePath);
} }
} }