mirror of
https://github.com/DrizzleTime/Foxel.git
synced 2026-09-05 15:47:00 +08:00
feat(storage): implement storage management API and enhance storage mode handling
This commit is contained in:
@@ -4,49 +4,134 @@ using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Foxel.Services.Storage.Providers;
|
||||
|
||||
[StorageProvider(StorageType.Local)]
|
||||
public class LocalStorageProvider(IConfigService configService) : IStorageProvider
|
||||
public class LocalStorageConfig
|
||||
{
|
||||
private readonly string _baseDirectory = Path.Combine(Directory.GetCurrentDirectory(), "Uploads");
|
||||
public string BasePath { get; set; } = string.Empty;
|
||||
|
||||
public string ServerUrl { get; set; } = string.Empty;
|
||||
|
||||
public string PublicBasePath { get; set; } = "/Uploads";
|
||||
}
|
||||
|
||||
[StorageProvider(StorageType.Local)]
|
||||
public class LocalStorageProvider : IStorageProvider
|
||||
{
|
||||
private readonly LocalStorageConfig _config;
|
||||
private readonly ILogger<LocalStorageProvider> _logger;
|
||||
|
||||
public LocalStorageProvider(LocalStorageConfig config, ILogger<LocalStorageProvider> logger)
|
||||
{
|
||||
_config = config;
|
||||
_logger = logger;
|
||||
if (string.IsNullOrWhiteSpace(_config.BasePath))
|
||||
{
|
||||
var defaultPath = Path.Combine(Directory.GetCurrentDirectory(), "DefaultUploads");
|
||||
_logger.LogWarning("LocalStorageConfig.BasePath 未配置,将使用默认路径: {DefaultPath}", defaultPath);
|
||||
throw new InvalidOperationException("LocalStorageConfig.BasePath 必须在配置中提供。");
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(_config.BasePath);
|
||||
}
|
||||
|
||||
public async Task<string> SaveAsync(Stream fileStream, string fileName, string contentType)
|
||||
{
|
||||
string currentDate = DateTime.Now.ToString("yyyy/MM");
|
||||
string folder = Path.Combine(_baseDirectory, currentDate);
|
||||
Directory.CreateDirectory(folder);
|
||||
try
|
||||
{
|
||||
string currentDate = DateTime.Now.ToString("yyyy/MM");
|
||||
string folder = Path.Combine(_config.BasePath, currentDate);
|
||||
Directory.CreateDirectory(folder);
|
||||
|
||||
string newFileName = fileName;
|
||||
string filePath = Path.Combine(folder, newFileName);
|
||||
string newFileName = fileName;
|
||||
string filePath = Path.Combine(folder, newFileName);
|
||||
|
||||
await using var output = new FileStream(filePath, FileMode.Create);
|
||||
await fileStream.CopyToAsync(output);
|
||||
return $"/Uploads/{currentDate}/{newFileName}";
|
||||
await using var output = new FileStream(filePath, FileMode.Create);
|
||||
await fileStream.CopyToAsync(output);
|
||||
return $"{_config.PublicBasePath.TrimEnd('/')}/{currentDate}/{newFileName}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "保存文件到本地存储时出错。BasePath: {BasePath}, FileName: {FileName}", _config.BasePath, fileName);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Task DeleteAsync(string storagePath)
|
||||
{
|
||||
string fullPath = Path.Combine(Directory.GetCurrentDirectory(), storagePath.TrimStart('/'));
|
||||
if (File.Exists(fullPath))
|
||||
File.Delete(fullPath);
|
||||
try
|
||||
{
|
||||
string relativePath = storagePath;
|
||||
if (!string.IsNullOrEmpty(_config.PublicBasePath) && storagePath.StartsWith(_config.PublicBasePath))
|
||||
{
|
||||
relativePath = storagePath.Substring(_config.PublicBasePath.Length);
|
||||
}
|
||||
|
||||
string fullPath = Path.Combine(_config.BasePath, relativePath.TrimStart('/'));
|
||||
|
||||
if (File.Exists(fullPath))
|
||||
{
|
||||
File.Delete(fullPath);
|
||||
_logger.LogInformation("已删除本地文件: {FullPath}", fullPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("尝试删除本地文件但文件未找到: {FullPath}", fullPath);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "删除本地文件时出错。StoragePath: {StoragePath}, BasePath: {BasePath}", storagePath,
|
||||
_config.BasePath);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public string GetUrl(string? storagePath)
|
||||
public string GetUrl(int pictureId,string? storagePath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(storagePath))
|
||||
return $"/images/unavailable.gif";
|
||||
|
||||
string serverUrl = configService["AppSettings:ServerUrl"];
|
||||
return $"{serverUrl}{storagePath}";
|
||||
string serverUrl = _config.ServerUrl.TrimEnd('/');
|
||||
return string.IsNullOrEmpty(serverUrl) ? storagePath : $"{serverUrl}{storagePath}";
|
||||
}
|
||||
|
||||
public Task<string> DownloadFileAsync(string storagePath)
|
||||
{
|
||||
string fullPath = Path.Combine(Directory.GetCurrentDirectory(), storagePath.TrimStart('/'));
|
||||
if (!File.Exists(fullPath))
|
||||
try
|
||||
{
|
||||
throw new FileNotFoundException($"找不到文件: {fullPath}");
|
||||
string relativePath = storagePath;
|
||||
if (!string.IsNullOrEmpty(_config.PublicBasePath) && storagePath.StartsWith(_config.PublicBasePath))
|
||||
{
|
||||
relativePath = storagePath.Substring(_config.PublicBasePath.Length);
|
||||
}
|
||||
|
||||
string fullPath = Path.Combine(_config.BasePath, relativePath.TrimStart('/'));
|
||||
|
||||
if (!File.Exists(fullPath))
|
||||
{
|
||||
_logger.LogError("尝试下载但文件未找到: {FullPath}", fullPath);
|
||||
throw new FileNotFoundException($"本地存储中找不到文件: {fullPath}", fullPath);
|
||||
}
|
||||
|
||||
string tempFileName = Path.GetRandomFileName();
|
||||
if (Path.HasExtension(fullPath))
|
||||
{
|
||||
tempFileName = Path.ChangeExtension(tempFileName, Path.GetExtension(fullPath));
|
||||
}
|
||||
|
||||
string tempFilePath = Path.Combine(Path.GetTempPath(), tempFileName);
|
||||
|
||||
File.Copy(fullPath, tempFilePath, true);
|
||||
_logger.LogInformation("已将文件 {FullPath} 复制到临时位置 {TempFilePath} 以供下载/处理", fullPath, tempFilePath);
|
||||
|
||||
return Task.FromResult(tempFilePath); // 返回临时文件的路径
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "下载本地文件时出错。StoragePath: {StoragePath}, BasePath: {BasePath}", storagePath,
|
||||
_config.BasePath);
|
||||
throw;
|
||||
}
|
||||
return Task.FromResult(fullPath);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user