BugFree.FileStorage 1.1.2026.303-beta1510

This is a prerelease version of BugFree.FileStorage.
dotnet add package BugFree.FileStorage --version 1.1.2026.303-beta1510
                    
NuGet\Install-Package BugFree.FileStorage -Version 1.1.2026.303-beta1510
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="BugFree.FileStorage" Version="1.1.2026.303-beta1510" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="BugFree.FileStorage" Version="1.1.2026.303-beta1510" />
                    
Directory.Packages.props
<PackageReference Include="BugFree.FileStorage" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add BugFree.FileStorage --version 1.1.2026.303-beta1510
                    
#r "nuget: BugFree.FileStorage, 1.1.2026.303-beta1510"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package BugFree.FileStorage@1.1.2026.303-beta1510
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=BugFree.FileStorage&version=1.1.2026.303-beta1510&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=BugFree.FileStorage&version=1.1.2026.303-beta1510&prerelease
                    
Install as a Cake Tool

BugFree.FileStorage

BugFree 文件存储抽象与 Provider 实现。

该模块用于将“对象存储(bucket + key)”统一映射到不同后端(本地文件系统 / OSS / COS 等),并提供:

  • 单文件上传/下载/删除/存在性
  • 预签名(用于参数防篡改与时效控制)
  • 四段式分片上传(Initiate/UploadPart/Complete/Abort)
  • Local 的磁盘选择与索引加速(可自愈)

功能特性

已实现 Provider

  • Local(本地文件系统):支持单文件对象操作与分片上传(本地临时分片目录合并)。
  • AliyunOss(阿里云 OSS):支持对象操作与分片上传(对接 OSS SDK)。
  • TencentCos(腾讯云 COS):支持对象操作与分片上传(对接 COS SDK)。
  • Minio:已提供配置模型与 ProviderType,但 Provider 目前为占位实现(尚未实现)。

安装方法

1) NuGet 引用

根据你的工程体系(应用层/组件层)选择引用:

  • BugFree.FileStorage

说明:仓库是多项目解决方案,若你仅编译单个项目,可能需要在构建时追加 GeneratePackageOnBuild=false 以绕过打包阶段对 README / 图标等文件的检查。

2) 依赖注入

  • 服务注册:services.AddFileStorageService();

使用示例

Minimal Example(伪代码,展示调用方式)

说明:以下示例只表达“接口使用意图”。具体类型名/命名空间以仓库实际代码为准。

  1. 获取服务(DI):
    • IFileStorageService / FileStorageService
  2. 上传:UploadAsync(bucket, key, stream, contentType, cancellationToken)
  3. 下载:GetAsync(bucket, key, cancellationToken)
  4. 删除:DeleteAsync(bucket, key, cancellationToken)
  5. 生成预签名:GeneratePresignedUrlAsync(bucket, key, expireSeconds, cancellationToken)

模块状态 / 能力矩阵

Provider 已实现 已测试(TestProject) 单文件 Upload/Get/Delete/Exists 分片上传 预签名 备注
Local 预签名仅返回 Query,不直接返回 Url
AliyunOss 通过 UserSecrets 读取密钥,未配置自动跳过
TencentCos 测试用例目前为空壳,需要补充集成测试
Minio 仅有配置模型,占位实现
能力 已实现 已测试 说明
Object:UploadAsync 单文件上传(流)
Object:GetAsync 下载(流)
Object:DeleteAsync 删除
Object:ExistsAsync 存在性
Presigned:GeneratePresignedUrlAsync Local 返回 Query;云端返回 Url 或 Query
Multipart:InitiateMultipartUploadAsync 分片上传初始化
Multipart:UploadPartAsync 上传分片
Multipart:CompleteMultipartUploadAsync 合并完成
Multipart:AbortMultipartUploadAsync 取消/清理

配置

配置模型为 FileStorageSettingConfig<FileStorageSetting>),支持加密存储。

示例(字段名与当前实现保持一致):

{
  "ProviderType": "Local | AliyunOss | TencentCos | Minio",
  "SingleFileMaxSize": 209715200,
  "MultipartMinSize": 67108864,
  "MultipartConcurrency": 4,
  "MultipartMaxCount": 200,
  "Local": {
    "StoragePath": [
      "D:/FileStorage",
      "E:/FileStorage"
    ],
    "SelectPolicy": "FreeSpaceFirst | Sequential | Random",
    "AccessUrl": "http://localhost/FileStorage/",
    "ParamEncryptKey": "<RANDOM_KEY>",
    "SignedUrlExpireSeconds": 3600,
    "DiskInfoCacheTtl": "00:00:20",
    "MultipartExpire": "02:00:00",
    "MultipartTempDirectoryMaxCount": 10000,
    "FileIndexType": "String | File"
  },
  "AliyunOss": {
    "Region": "cn-hangzhou",
    "Endpoint": "https://oss-cn-hangzhou.aliyuncs.com",
    "BucketName": "your-bucket",
    "AccessKeyId": "<YOUR_KEY>",
    "AccessKeySecret": "<YOUR_SECRET>",
    "AccessUrl": "https://your-cdn.example.com/",
    "SignedUrlExpireSeconds": 3600
  },
  "TencentCos": {
    "AppId": "<APPID>",
    "Region": "ap-guangzhou",
    "SecretId": "<SECRET_ID>",
    "SecretKey": "<SECRET_KEY>",
    "BucketName": "your-bucket-123456",
    "AccessUrl": "https://your-bucket-123456.cos.ap-guangzhou.myqcloud.com",
    "IsHttps": true,
    "ConnectionTimeout": 45000,
    "ReadWriteTimeout": 45000,
    "SignedUrlExpireSeconds": 3600
  },
  "Minio": {
    "Endpoint": "http://127.0.0.1:9000",
    "AccessKey": "<ACCESS_KEY>",
    "SecretKey": "<SECRET_KEY>",
    "BucketName": "your-bucket",
    "AccessUrl": "https://your-minio.example.com/",
    "UseSSL": false,
    "SignedUrlExpireSeconds": 3600
  }
}

Local 相关配置说明

  • FileIndexType:索引类型
    • String:使用 StringIndex(bucket 携带 diskId|bucketName
    • File:使用 FileIndex(落盘到 index_{diskId}.idx

Local Provider 索引(bucket + key → disk)

本地存储为提升定位性能,提供两种索引实现(见 BugFree.FileStorage.Provider.Local.Index):

  1. FileIndex(落盘索引)
  • 在内存维护 bucket + '\0' + key 的集合,并按磁盘分别落盘为 index_{diskId}.idx
  • 优点:定位快、可跨进程/重启恢复索引
  • 缺点:需要刷盘(有 IO)
  1. StringIndex(字符串索引,不落盘)
  • 不维护映射表,而是将 diskId 编码进 bucket 中,通过解析 bucket 直接定位磁盘
  • bucket 约定格式:"{diskId}|{bucketName}"
  • 优点:零维护、无刷盘
  • 缺点:bucket 会暴露 diskId(不建议直接对外部用户开放)

索引属于“加速结构”,允许与真实文件状态短暂不一致:

  • 索引命中但文件不存在:Local 会清理索引(自愈)。
  • 索引未命中但扫描找到:Local 会写回索引(自愈)。

预签名(GeneratePresignedUrlAsync)

1) 本地 Provider 不返回 Url

Local 仅返回签名数据(例如 Query 中的 expireAtsign),不直接返回可访问 Url。调用方需要自行拼装预览/下载入口,例如:

  • 访问入口(示例):/filestorage/preview?bucket={bucket}&key={key}&expireAt={expireAt}&sign={sign}

说明:访问入口的路由/控制器由业务方自行实现,FileStorage 模块只负责生成签名参数。

2) Local 固定签名原文格式

Local 签名原文固定为:

  • raw = "{bucket}:{key}:{expireAtUnixSeconds}"

签名算法:当前实现使用 HMACMD5,对应的密钥优先取 Local.ParamEncryptKey

注意:

  • ParamEncryptKey 属于机密信息,禁止下发/禁止日志输出。
  • 若未配置 ParamEncryptKey(例如测试环境临时构造配置),实现会退化为使用 key 派生密钥以保证兼容,但安全性更弱。

3) 如何验证(建议)

在你自定义的预览/下载入口中,建议按以下步骤验证:

  1. 解析 Query:取 expireAtsign
  2. 校验过期:当前时间(UTC)不得超过 expireAt
  3. 重算签名:按同样的 raw 规则 + 同样的密钥(优先 Local.ParamEncryptKey)计算签名,对比 sign
  4. 通过 bucket/key 调用 GetAsync 读取流并返回内容。

安全提醒:预签名只解决“防篡改 + 时效性”,不等价于业务鉴权;若对象属于私有资源,入口仍应结合登录态/权限/租户隔离等校验。

测试覆盖情况

说明:云端 Provider 通常依赖真实账号/网络/存储桶,因此默认不在仓库中提供可直接运行的集成测试(避免泄露密钥)。

  • 已测试(TestProject)

    • Local:TestProject/FileStorage/LocalFileStorageTest.cs
      • 单文件上传/存在性/URL/删除
      • 分片上传:初始化/上传分片/完成合并/取消
      • 多路径策略:Sequential/Random/FreeSpaceFirst
    • AliyunOss:TestProject/FileStorage/AliyunOssFileStorageTest.cs
      • 通过 UserSecrets 读取密钥;未配置时自动跳过
      • 单文件:上传/存在/预签名/删除
      • 分片:初始化/上传分片/完成/取消
  • 未测试(仅有空壳用例,需要补充集成测试)

    • TencentCos:TestProject/FileStorage/TencentCosFileStorageTest.cs
    • Minio:TestProject/FileStorage/MinioFileStorageTest.cs

如何运行测试

  • 运行全部测试:执行 TestProject 工程测试即可。
  • 仅运行本地存储:筛选 TestFixture/用例名称包含 LocalFileStorageTest。

注意事项

  • 不要提交真实密钥/密码/令牌;示例中请使用占位符。
  • 本地存储(Local)当前实现不会在 GeneratePresignedUrlAsync 中返回可直接访问的 Url(PresignedUrlResult.Url 为空),仅返回签名参数;访问入口需由业务方自行实现并在入口里校验 expireAt/sign
  • 单文件上传会受 SingleFileMaxSize 限制;超过阈值请使用分片上传相关接口。

Local 路径安全与 DiskId

  • LocalDiskSelectorBase.CombineAndEnsureSafe(...) 会对 bucket/key 做路径片段规范化与安全校验,阻止路径穿越(../盘符等)。
  • diskId 为磁盘根路径的稳定标识(基于根路径计算),用于:
    • 分片元数据 context.Metadata[DiskId] 定位落盘磁盘
    • StringIndex bucket 前缀定位
    • FileIndex 的索引文件命名 index_{diskId}.idx

Roadmap

事项 状态 说明
Minio Provider 实现 当前为占位实现,需要补对象操作与分片上传
TencentCos 集成测试 目前用例为空壳,建议加可选的 UserSecrets 驱动测试
Local 预签名统一入口示例(Web API / Razor Pages) 仓库只提供签名参数生成,访问入口由业务实现

贡献指南

  • Issue:请提供复现步骤、期望结果、实际结果、运行环境(OS/.NET/Provider)以及必要日志(注意脱敏)。
  • PR:尽量保持改动面小;新增 Provider/能力时同步更新本 README 的能力矩阵与 Roadmap。

许可证(License)

以仓库根目录的 License 文件为准。

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.2026.303-beta1510 38 3/3/2026
1.1.2026.127-beta1557 45 3/3/2026
1.1.2026.115-beta1541 64 1/15/2026
1.0.2026.107-beta1426 59 1/7/2026
1.0.2026.106-beta1144 57 1/6/2026
1.0.2025.1224-beta1658 140 12/24/2025
1.0.2025.1224-beta1527 134 12/24/2025
1.0.2025.1224-beta1412 137 12/24/2025