139 lines
4.7 KiB
C#
139 lines
4.7 KiB
C#
using MiAssessment.Admin.Business.Models.Upload;
|
||
using MiAssessment.Admin.Business.Services.Interfaces;
|
||
using Microsoft.AspNetCore.Hosting;
|
||
using Microsoft.Extensions.Logging;
|
||
|
||
namespace MiAssessment.Admin.Business.Services.Storage;
|
||
|
||
/// <summary>
|
||
/// 本地存储提供者
|
||
/// 将文件保存到 wwwroot/uploads/{yyyy}/{MM}/{dd}/ 目录
|
||
/// </summary>
|
||
public class LocalStorageProvider : IStorageProvider
|
||
{
|
||
private readonly IWebHostEnvironment _environment;
|
||
private readonly ILogger<LocalStorageProvider> _logger;
|
||
private const string UploadBasePath = "uploads";
|
||
|
||
public LocalStorageProvider(
|
||
IWebHostEnvironment environment,
|
||
ILogger<LocalStorageProvider> logger)
|
||
{
|
||
_environment = environment;
|
||
_logger = logger;
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public string StorageType => "1";
|
||
|
||
/// <inheritdoc />
|
||
public bool SupportsDirectUpload => false;
|
||
|
||
/// <inheritdoc />
|
||
public async Task<UploadResult> UploadAsync(Stream fileStream, string fileName, string contentType)
|
||
{
|
||
try
|
||
{
|
||
// 生成日期目录路径: uploads/2026/01/19/
|
||
var now = DateTime.Now;
|
||
var datePath = Path.Combine(
|
||
now.Year.ToString(),
|
||
now.Month.ToString("D2"),
|
||
now.Day.ToString("D2"));
|
||
|
||
// 生成唯一文件名
|
||
var uniqueFileName = GenerateUniqueFileName(fileName);
|
||
|
||
// 构建完整的物理路径
|
||
var relativePath = Path.Combine(UploadBasePath, datePath);
|
||
var physicalPath = Path.Combine(_environment.WebRootPath, relativePath);
|
||
|
||
// 确保目录存在
|
||
EnsureDirectoryExists(physicalPath);
|
||
|
||
// 完整文件路径
|
||
var fullFilePath = Path.Combine(physicalPath, uniqueFileName);
|
||
|
||
// 保存文件
|
||
await using var fileStreamOutput = new FileStream(fullFilePath, FileMode.Create, FileAccess.Write);
|
||
await fileStream.CopyToAsync(fileStreamOutput);
|
||
|
||
// 生成相对URL路径 (使用正斜杠)
|
||
var url = $"/{UploadBasePath}/{datePath.Replace(Path.DirectorySeparatorChar, '/')}/{uniqueFileName}";
|
||
|
||
_logger.LogInformation("本地存储上传成功: {FileName} -> {Url}", fileName, url);
|
||
|
||
return UploadResult.Ok(url);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "本地存储上传失败: {FileName}", fileName);
|
||
return UploadResult.Fail($"保存文件失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public Task<bool> DeleteAsync(string fileUrl)
|
||
{
|
||
try
|
||
{
|
||
if (string.IsNullOrWhiteSpace(fileUrl))
|
||
{
|
||
return Task.FromResult(false);
|
||
}
|
||
|
||
// 将URL转换为物理路径
|
||
// URL格式: /uploads/2026/01/19/xxx.jpg
|
||
var relativePath = fileUrl.TrimStart('/').Replace('/', Path.DirectorySeparatorChar);
|
||
var physicalPath = Path.Combine(_environment.WebRootPath, relativePath);
|
||
|
||
if (File.Exists(physicalPath))
|
||
{
|
||
File.Delete(physicalPath);
|
||
_logger.LogInformation("本地存储删除成功: {Url}", fileUrl);
|
||
return Task.FromResult(true);
|
||
}
|
||
|
||
_logger.LogWarning("本地存储删除失败,文件不存在: {Url}", fileUrl);
|
||
return Task.FromResult(false);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "本地存储删除失败: {Url}", fileUrl);
|
||
return Task.FromResult(false);
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public Task<PresignedUrlResponse?> GetPresignedUploadUrlAsync(string fileName, string contentType, int expiresInSeconds = 600)
|
||
{
|
||
// 本地存储不支持客户端直传,返回null
|
||
_logger.LogDebug("本地存储不支持客户端直传");
|
||
return Task.FromResult<PresignedUrlResponse?>(null);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成唯一文件名
|
||
/// 格式: {timestamp}_{guid}{extension}
|
||
/// </summary>
|
||
private static string GenerateUniqueFileName(string originalFileName)
|
||
{
|
||
var extension = Path.GetExtension(originalFileName).ToLowerInvariant();
|
||
var timestamp = DateTime.Now.ToString("yyyyMMddHHmmssfff");
|
||
var guid = Guid.NewGuid().ToString("N")[..8]; // 取GUID前8位
|
||
return $"{timestamp}_{guid}{extension}";
|
||
}
|
||
|
||
/// <summary>
|
||
/// 确保目录存在,不存在则创建
|
||
/// </summary>
|
||
private void EnsureDirectoryExists(string path)
|
||
{
|
||
if (!Directory.Exists(path))
|
||
{
|
||
Directory.CreateDirectory(path);
|
||
_logger.LogDebug("创建目录: {Path}", path);
|
||
}
|
||
}
|
||
}
|