![]() |
VOOZH | about |
dotnet add package BugFree.FileStorage --version 1.1.2026.325-beta1128
NuGet\Install-Package BugFree.FileStorage -Version 1.1.2026.325-beta1128
<PackageReference Include="BugFree.FileStorage" Version="1.1.2026.325-beta1128" />
<PackageVersion Include="BugFree.FileStorage" Version="1.1.2026.325-beta1128" />Directory.Packages.props
<PackageReference Include="BugFree.FileStorage" />Project file
paket add BugFree.FileStorage --version 1.1.2026.325-beta1128
#r "nuget: BugFree.FileStorage, 1.1.2026.325-beta1128"
#:package BugFree.FileStorage@1.1.2026.325-beta1128
#addin nuget:?package=BugFree.FileStorage&version=1.1.2026.325-beta1128&prereleaseInstall as a Cake Addin
#tool nuget:?package=BugFree.FileStorage&version=1.1.2026.325-beta1128&prereleaseInstall as a Cake Tool
BugFree 文件存储抽象与 Provider 实现。
该模块用于将“对象存储(bucket + key)”统一映射到不同后端(本地文件系统 / OSS / COS 等),并提供:
根据你的工程体系(应用层/组件层)选择引用:
BugFree.FileStorage说明:仓库是多项目解决方案,若你仅编译单个项目,可能需要在构建时追加
GeneratePackageOnBuild=false以绕过打包阶段对 README / 图标等文件的检查。
services.AddFileStorageService();说明:以下示例只表达“接口使用意图”。具体类型名/命名空间以仓库实际代码为准。
IFileStorageService / FileStorageServiceUploadAsync(bucket, key, stream, contentType, cancellationToken)GetAsync(bucket, key, cancellationToken)DeleteAsync(bucket, key, cancellationToken)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 | ✔ | ✔ | 取消/清理 |
配置模型为 FileStorageSetting(Config<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
}
}
FileIndexType:索引类型
String:使用 StringIndex(bucket 携带 diskId|bucketName)File:使用 FileIndex(落盘到 index_{diskId}.idx)本地存储为提升定位性能,提供两种索引实现(见 BugFree.FileStorage.Provider.Local.Index):
FileIndex(落盘索引)bucket + '\0' + key 的集合,并按磁盘分别落盘为 index_{diskId}.idxStringIndex(字符串索引,不落盘)diskId 编码进 bucket 中,通过解析 bucket 直接定位磁盘"{diskId}|{bucketName}"索引属于“加速结构”,允许与真实文件状态短暂不一致:
Local 仅返回签名数据(例如 Query 中的 expireAt 与 sign),不直接返回可访问 Url。调用方需要自行拼装预览/下载入口,例如:
/filestorage/preview?bucket={bucket}&key={key}&expireAt={expireAt}&sign={sign}说明:访问入口的路由/控制器由业务方自行实现,FileStorage 模块只负责生成签名参数。
Local 签名原文固定为:
raw = "{bucket}:{key}:{expireAtUnixSeconds}"签名算法:当前实现使用 HMACMD5,对应的密钥优先取 Local.ParamEncryptKey。
注意:
ParamEncryptKey 属于机密信息,禁止下发/禁止日志输出。ParamEncryptKey(例如测试环境临时构造配置),实现会退化为使用 key 派生密钥以保证兼容,但安全性更弱。在你自定义的预览/下载入口中,建议按以下步骤验证:
expireAt 与 sign。expireAt。raw 规则 + 同样的密钥(优先 Local.ParamEncryptKey)计算签名,对比 sign。bucket/key 调用 GetAsync 读取流并返回内容。安全提醒:预签名只解决“防篡改 + 时效性”,不等价于业务鉴权;若对象属于私有资源,入口仍应结合登录态/权限/租户隔离等校验。
说明:云端 Provider 通常依赖真实账号/网络/存储桶,因此默认不在仓库中提供可直接运行的集成测试(避免泄露密钥)。
已测试(TestProject)
未测试(仅有空壳用例,需要补充集成测试)
GeneratePresignedUrlAsync 中返回可直接访问的 Url(PresignedUrlResult.Url 为空),仅返回签名参数;访问入口需由业务方自行实现并在入口里校验 expireAt/sign。SingleFileMaxSize 限制;超过阈值请使用分片上传相关接口。LocalDiskSelectorBase.CombineAndEnsureSafe(...) 会对 bucket/key 做路径片段规范化与安全校验,阻止路径穿越(../盘符等)。diskId 为磁盘根路径的稳定标识(基于根路径计算),用于:
context.Metadata[DiskId] 定位落盘磁盘StringIndex bucket 前缀定位FileIndex 的索引文件命名 index_{diskId}.idx| 事项 | 状态 | 说明 |
|---|---|---|
| Minio Provider 实现 | ❌ | 当前为占位实现,需要补对象操作与分片上传 |
| TencentCos 集成测试 | ❌ | 目前用例为空壳,建议加可选的 UserSecrets 驱动测试 |
| Local 预签名统一入口示例(Web API / Razor Pages) | ❌ | 仓库只提供签名参数生成,访问入口由业务实现 |
以仓库根目录的 License 文件为准。
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 net8.0 is compatible. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 was computed. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
This package is not used by any NuGet packages.
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.1.2026.325-beta1128 | 74 | 3/25/2026 |
| 1.1.2026.325-beta1042 | 53 | 3/25/2026 |
| 1.1.2026.325-beta1036 | 52 | 3/25/2026 |
| 1.1.2026.325-beta1013 | 57 | 3/25/2026 |
| 1.1.2026.303-beta1510 | 66 | 3/3/2026 |
| 1.1.2026.127-beta1557 | 70 | 3/3/2026 |
| 1.1.2026.115-beta1541 | 85 | 1/15/2026 |
| 1.0.2026.107-beta1426 | 77 | 1/7/2026 |
| 1.0.2026.106-beta1144 | 74 | 1/6/2026 |
| 1.0.2025.1224-beta1658 | 155 | 12/24/2025 |
| 1.0.2025.1224-beta1527 | 149 | 12/24/2025 |
| 1.0.2025.1224-beta1412 | 157 | 12/24/2025 |