MoneyTree.Storage 文件存储
📋 概述
MoneyTree.Storage 提供统一的文件存储抽象层,屏蔽底层云服务商差异。开发者通过 IFileStorage 接口操作文件,支持本地磁盘、MinIO、阿里云 OSS、腾讯云 COS 和七牛云等多种后端,切换提供者只需改配置无需改代码。
| 属性 |
说明 |
| NuGet 包 |
MoneyTree.Storage |
| 外部依赖 |
MoneyTree.Core |
| 定位 |
统一文件存储抽象与多云后端集成 |
📦 包体系结构
MoneyTree.Storage ← 核心包:IFileStorage 接口 + 本地存储
├── MoneyTree.Storage.Minio ← 扩展包:MinIO 对象存储
├── MoneyTree.Storage.AliyunOss ← 扩展包:阿里云 OSS
├── MoneyTree.Storage.TencentCos ← 扩展包:腾讯云 COS
└── MoneyTree.Storage.Qiniu ← 扩展包:七牛云存储
🏗️ 项目文件结构
MoneyTree.Storage/
├── MoneyTree.Storage.csproj
├── GlobalUsings.cs
├── Abstractions/
│ └── IFileStorage.cs # 统一文件存储接口
├── Core/
│ ├── FileStorageOptions.cs # 全局配置选项
│ └── FileMetadata.cs # 文件元数据模型
├── Providers/
│ └── LocalFileStorage.cs # 本地磁盘存储
└── Extensions/
├── StorageBuilder.cs # 流畅配置构造器
└── StorageExtensions.cs # DI 注册扩展
🎯 设计原则
| 原则 |
说明 |
| 统一接口 |
所有提供者实现同一个 IFileStorage,切换提供者只需改配置 |
| 流式处理 |
上传/下载均使用 Stream,避免大文件撑爆内存 |
| 元数据丰富 |
自动记录文件名、大小、MIME 类型、MD5 哈希、上传时间 |
| URL 生成 |
支持预签名 URL(私有 Bucket)和公开 URL |
🚀 快速开始
本地存储(开发环境)
// Program.cs
using MoneyTree.Storage.Extensions;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMoneyTreeStorage(s => s.UseLocal("./uploads"));
MinIO 存储
builder.Services.AddMoneyTreeStorage(s =>
{
s.UseMinio(
endpoint: "minio.example.com:9000",
accessKey: "your-key",
secretKey: "your-secret",
bucketName: "myapp",
useSsl: true);
});
阿里云 OSS
builder.Services.AddMoneyTreeStorage(s =>
{
s.UseAliyunOss(
endpoint: "oss-cn-hangzhou.aliyuncs.com",
accessKeyId: "your-ak",
accessKeySecret: "your-sk",
bucketName: "my-bucket");
});
腾讯云 COS
builder.Services.AddMoneyTreeStorage(s =>
{
s.UseTencentCos(
region: "ap-guangzhou",
secretId: "your-secret-id",
secretKey: "your-secret-key",
bucketName: "my-bucket");
});
七牛云
builder.Services.AddMoneyTreeStorage(s =>
{
s.UseQiniu(
accessKey: "your-ak",
secretKey: "your-sk",
bucketName: "my-bucket",
baseUrl: "https://cdn.example.com");
});
📖 使用示例
文件上传
public class AvatarService
{
private readonly IFileStorage _storage;
public AvatarService(IFileStorage storage)
{
_storage = storage;
}
public async Task<string> UploadAvatarAsync(string userId, Stream image)
{
var key = $"avatars/{userId}.jpg";
var metadata = await _storage.UploadAsync(key, image, "image/jpeg");
return await _storage.GetPublicUrlAsync(key);
}
}
文件下载终结点
app.MapGet("/api/files/{**key}", async (string key, IFileStorage storage) =>
{
var stream = await storage.DownloadAsync(key);
if (stream is null) return Results.NotFound();
var metadata = await storage.GetMetadataAsync(key);
return Results.File(stream, metadata?.ContentType ?? "application/octet-stream");
});
文件删除和列表
// 删除文件
await _storage.DeleteAsync("avatars/user-001.jpg");
// 列出目录下的所有文件
var files = await _storage.ListAsync("avatars/");
// 获取文件元数据
var metadata = await _storage.GetMetadataAsync("reports/monthly.xlsx");
📋 IFileStorage 接口说明
| 方法 |
说明 |
UploadAsync(key, stream, contentType) |
上传文件,返回元数据 |
DownloadAsync(key) |
下载文件流 |
DeleteAsync(key) |
删除文件 |
DeleteAsync(keys) |
批量删除文件 |
ListAsync(prefix) |
列出指定前缀的文件列表 |
GetMetadataAsync(key) |
获取文件元数据 |
GetPublicUrlAsync(key) |
获取公开访问 URL |
GetPresignedUrlAsync(key, expiry) |
生成预签名 URL(私有 Bucket) |
ExistsAsync(key) |
检查文件是否存在 |
| 属性 |
类型 |
说明 |
Key |
string |
文件唯一标识(路径) |
FileName |
string |
文件名称 |
ContentType |
string |
MIME 类型 |
Size |
long |
文件大小(字节) |
Md5Hash |
string? |
MD5 哈希值 |
UploadedAt |
DateTime |
上传时间 |
LastModified |
DateTime |
最后修改时间 |
StorageProvider |
string |
存储提供者名称 |
🔌 存储后端对比
| 后端 |
开发环境 |
生产环境 |
特点 |
| 本地磁盘 |
✅ 推荐 |
❌ |
零依赖,快速启动 |
| MinIO |
✅ |
✅ |
开源,S3 兼容,可自建 |
| 阿里云 OSS |
❌ |
✅ |
国内主流,CDN 加速 |
| 腾讯云 COS |
❌ |
✅ |
国内主流,数据处理 |
| 七牛云 |
❌ |
✅ |
CDN 加速,图片处理 |