Files
IOT_API/Service/Implement/LocalFileStorageProvider.cs
T

121 lines
5.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Model;
using Model.Dto.System;
using Model.Entity.System;
using Service.Interface;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
namespace Service.Implement
{
/// <summary>
/// 本地文件系统存储 Provider
/// 落地策略:存到 BasePath/{Bucket?}/{yyyy}/{MM}/{dd}/{guid}{ext}URL = {Endpoint}/{Bucket?}/{yyyy}/{MM}/{dd}/{guid}{ext}
/// wwwroot/uploads 由 Program.UseStaticFiles 提供访问;MinIO/OSS 实现见 IFileStorageProvider 扩展点
/// </summary>
public class LocalFileStorageProvider : IFileStorageProvider
{
public string ProviderName => "local";
public async Task<Result<FileUploadResultDto>> UploadAsync(Stream stream, string originalName, long size, FileStorageConfigEntity config)
{
if (config == null) return Result<FileUploadResultDto>.Error("存储配置不能为空");
if (stream == null || !stream.CanRead) return Result<FileUploadResultDto>.Error("文件流不可读");
try
{
var basePath = ResolveBasePath(config.BasePath);
if (string.IsNullOrWhiteSpace(basePath))
return Result<FileUploadResultDto>.Error("Local 存储必须配置 BasePath");
var ext = Path.GetExtension(originalName)?.ToLowerInvariant() ?? "";
var now = DateTime.Now;
var datePath = $"{now:yyyy}/{now:MM}/{now:dd}";
var fileName = $"{Guid.NewGuid():N}{ext}";
var storedFileName = string.IsNullOrWhiteSpace(config.Bucket)
? $"{datePath}/{fileName}"
: $"{config.Bucket}/{datePath}/{fileName}";
var dir = Path.Combine(basePath,
string.IsNullOrWhiteSpace(config.Bucket) ? datePath : Path.Combine(config.Bucket, datePath));
Directory.CreateDirectory(dir);
var fullPath = Path.Combine(dir, fileName);
using (var fs = new FileStream(fullPath, FileMode.CreateNew, FileAccess.Write, FileShare.None))
{
await stream.CopyToAsync(fs);
}
var url = BuildUrl(config.Endpoint, config.Bucket, storedFileName);
return Result<FileUploadResultDto>.Success(new FileUploadResultDto
{
FileName = storedFileName,
Url = url,
Ext = ext,
Size = size,
Provider = ProviderName
});
}
catch (Exception ex)
{
return Result<FileUploadResultDto>.Error("本地文件上传失败", ex);
}
}
public Task<Result> DeleteAsync(string storedFileName, FileStorageConfigEntity config)
{
if (config == null || string.IsNullOrWhiteSpace(storedFileName))
return Task.FromResult(Result.Error("存储配置或文件名不能为空"));
try
{
var basePath = ResolveBasePath(config.BasePath);
var fullPath = Path.Combine(basePath, storedFileName.Replace('/', Path.DirectorySeparatorChar));
if (File.Exists(fullPath)) File.Delete(fullPath);
return Task.FromResult(Result.Success());
}
catch (Exception ex)
{
return Task.FromResult(Result.Error("删除本地文件失败", ex));
}
}
public Task<Result> TestConnectionAsync(FileStorageConfigEntity config)
{
if (config == null) return Task.FromResult(Result.Error("存储配置不能为空"));
try
{
var basePath = ResolveBasePath(config.BasePath);
if (string.IsNullOrWhiteSpace(basePath))
return Task.FromResult(Result.Error("Local 存储必须配置 BasePath"));
Directory.CreateDirectory(basePath);
var testFile = Path.Combine(basePath, $".storage_test_{Guid.NewGuid():N}");
File.WriteAllText(testFile, "ok");
File.Delete(testFile);
return Task.FromResult(Result.Success());
}
catch (Exception ex)
{
return Task.FromResult(Result.Error("Local 连接测试失败:" + ex.Message));
}
}
// ==================== 私有工具 ====================
private static string ResolveBasePath(string? basePath)
{
if (string.IsNullOrWhiteSpace(basePath)) return string.Empty;
// 相对路径以运行目录为基准(如 wwwroot/uploads
return Path.IsPathRooted(basePath) ? basePath : Path.Combine(AppContext.BaseDirectory, basePath);
}
private static string BuildUrl(string? endpoint, string? bucket, string storedFileName)
{
var segs = new List<string>();
if (!string.IsNullOrWhiteSpace(endpoint)) segs.Add(endpoint.TrimEnd('/'));
if (!string.IsNullOrWhiteSpace(bucket)) segs.Add(bucket.Trim('/'));
segs.Add(storedFileName);
return string.Join('/', segs);
}
}
}