XiHan.Framework.ObjectStorage.S3
5.0.0-preview.1
Prefix Reserved
See the version list below for details.
dotnet add package XiHan.Framework.ObjectStorage.S3 --version 5.0.0-preview.1
NuGet\Install-Package XiHan.Framework.ObjectStorage.S3 -Version 5.0.0-preview.1
<PackageReference Include="XiHan.Framework.ObjectStorage.S3" Version="5.0.0-preview.1" />
<PackageVersion Include="XiHan.Framework.ObjectStorage.S3" Version="5.0.0-preview.1" />
<PackageReference Include="XiHan.Framework.ObjectStorage.S3" />
paket add XiHan.Framework.ObjectStorage.S3 --version 5.0.0-preview.1
#r "nuget: XiHan.Framework.ObjectStorage.S3, 5.0.0-preview.1"
#:package XiHan.Framework.ObjectStorage.S3@5.0.0-preview.1
#addin nuget:?package=XiHan.Framework.ObjectStorage.S3&version=5.0.0-preview.1&prerelease
#tool nuget:?package=XiHan.Framework.ObjectStorage.S3&version=5.0.0-preview.1&prerelease
XiHan.Framework.ObjectStorage.S3
对象存储的 S3 Provider(Ring 3,role: provider)。提供 objectstorage.blob@1(RFC-0012):容器即桶,键即对象键;经 AWS SDK 访问 Amazon S3 与兼容服务(MinIO、阿里云 OSS、腾讯云 COS、Cloudflare R2 等)。
builder.AddXiHan(xihan => xihan.UseFeature<S3ObjectStorageFeature>());
{ "XiHan": { "ObjectStorage": { "S3": { "ServiceUrl": "http://localhost:9000", "Region": "us-east-1", "AccessKey": "rustfsadmin", "SecretKey": "rustfsadmin", "ForcePathStyle": true, "EnsureBuckets": false, "HealthBucket": "uploads" } } } }
绑定 objectstorage(blob,可选;类型取 RFC-0016 的绑定类型词表)存在时逐项覆盖配置:主值是服务端点,属性 region、accessKey、secretKey、forcePathStyle;空白的主值与属性不算给出(RFC-0016「资源绑定与配置节」)。
规则
ServiceUrl为空时按Region访问 Amazon S3;有值时指向兼容服务,Region作签名区域(默认us-east-1),并只在服务端要求时计算 / 校验校验和(多数兼容服务不支持分块尾部校验和)。AccessKey/SecretKey都为空时走 SDK 默认凭据链(环境变量、配置文件、实例角色)。应用自己注册的IAmazonS3优先于本特性创建的客户端。- 写入:
Overwrite = false先 HEAD 再带If-None-Match: *写入,由服务端保证原子,已存在返回OBJECTSTORAGE_CONFLICT_001;服务是否认条件写入要先探明(下一条),探明之前不覆盖写入不放行;不可定位的内容流先落临时文件(关闭即删)取得长度,不整体装进内存。EnsureBuckets = true时桶不存在就建桶再重试一次,默认由基础设施预先建桶。 - 条件写入探测(ADR-0089、ADR-0090):
ConditionalWrites(缺省true)开启时,在HealthBucket(这时必填,没配即装配失败 / 命名实例OBJECTSTORAGE_VALIDATION_003)里对探测键.xihan/probe/conditional-write带If-None-Match: *写两次,第二次必须 412;提供分片上传时再以同一个键带条件完成一次分片上传,必须 412,随即放弃该上传。探测键是空对象、写入后不删除——任何一次 412 都证明服务认条件写入,所以并发启动、反复启动、后台重试结论相同;凭据只需要该键的s3:PutObject与s3:AbortMultipartUpload;它在保留前缀.xihan(BlobKey.ReservedSegment)下,不在键文法之内,业务列举跳过它(ADR-0090;ADR-0089 起就是这个名字,桶里已有的探测键无需迁移,也不必删除)。单次探测以HealthProbeIntervalSeconds为超时。 - 探测的时机与结论(ADR-0090、ADR-0093):缺省实例在启动时(与健康项的首次探测并行)、命名实例在建出时探一次。探明不认(第二次没被拒或 501,
OBJECTSTORAGE_UNAVAILABLE_003)、被拒绝(403:凭据无效、只读或无权写探测键,OBJECTSTORAGE_DENIED_002)、探测桶不存在(OBJECTSTORAGE_NOTFOUND_001,开EnsureBuckets时先建桶)都是配置问题,启动失败(KERNEL_HOSTING_002← 对应码)或不建命名实例(注册表返回对应码)。只有连不上或超时(OBJECTSTORAGE_UNAVAILABLE_001)是"尚未确认":照常启动、照常建出实例,在后台重试——1 秒后,之后间隔加倍,最长HealthProbeIntervalSeconds,按注入的TimeProvider计时,有了结论即停,应用停止或实例释放时也停。尚未确认时Overwrite = false的写入与分片上传的发起、完成:有探测在进行就等它,没有而距上一次探测满 1 秒就地再探一次(每个实例同一时刻只有一个探测),仍未确认返回OBJECTSTORAGE_UNAVAILABLE_004(HTTP 503,reason是最近一次探测失败的错误码),不访问写入目标、不退回先查再写;服务恢复后自动通过,不必重启。启动之后才探明不认:不覆盖写入一律OBJECTSTORAGE_UNAVAILABLE_003,记 Error 日志,换服务或关闭条件写入后重启。启动时连不上、连上时才被拒绝或缺桶(凭据已被吊销、桶被删,或配置本来就错):应用不停止,仍是尚未确认、照常重试,不覆盖写入仍是OBJECTSTORAGE_UNAVAILABLE_004(reason为DENIED_002/NOTFOUND_001),健康项 Failed,记 Error 日志;权限改好、桶建好后自动通过(已定,ADR-0097:不改为停止重试或只记 Degraded)。已确认之后凭据被吊销不再探测,读写按下面的错误映射失败、连通性 HEAD 让健康项 Degraded。实测 RustFS 1.0.4、MinIO 通过,Garage v2.5.0、Zenko CloudServer 8.2.7 不认条件写入、启动失败(Zenko 于 2026-11-02 补跑,见 ADR-0093)。 - 云厂商的兼容服务(ADR-0090):阿里云 OSS、腾讯云 COS、Cloudflare R2 等同样只用本包,不为各家的私有请求头(OSS 的
x-oss-forbid-overwrite、COS 的x-cos-forbid-overwrite)做适配,不覆盖写入只认标准的If-None-Match: *。这几家是否认它框架没有实测,接入前要实测:直接用缺省的探测(启动时探明不认即启动失败,OBJECTSTORAGE_UNAVAILABLE_003;连不上时看健康项数据conditionalWrites是否变成confirmed),或用 curl 对同一个键发两次带If-None-Match: *的PutObject,第二次必须 412。不认就设ConditionalWrites = false(命名实例的设置同名,可按实例关闭),后果见下一条。 - 关闭条件写入(
ConditionalWrites = false):只用于明确不需要不覆盖写入的应用(只覆盖写入、凭据只读,或服务实测不认条件写入)。不探测、HealthBucket可不配;Overwrite = false的写入与分片上传的发起、完成一律返回OBJECTSTORAGE_UNAVAILABLE_003,不访问存储——不做没有保证的先查再写;"只在不存在时写入"在这个存储上不再可用,需要它的业务改用不会冲突的键(如带随机后缀)或改为覆盖写入(BlobPutOptions缺省就是覆盖写入)。 - 读取:
Get的内容流释放时连同响应一起释放;Head返回长度、内容类型、ETag、修改时间与去掉x-amz-meta-前缀的自定义元数据。 - 批量删除:S3 的删除不报告对象是否存在过,先逐键 HEAD 定存在性(同一批里重复的键只有第一次算存在),再把存在的键按每批 1000 个
DeleteObjects;删除失败只影响本次负责删除的项。 - 列举:
ListObjectsV2按前缀分页(S3 保证键的序数顺序),续页标记就是 S3 的 continuation token;条目只有键、长度、ETag 与修改时间,内容类型为默认值、自定义元数据为空,需要时再Head;不符合键文法的对象(以/结尾的"目录"占位、保留前缀.xihan下的框架对象如探测键)跳过,所以一页可能少于PageSize条、甚至为空而仍带续页标记;桶不存在视作空容器。 - 错误:不存在的键与桶映射为
OBJECTSTORAGE_NOTFOUND_001,其余服务端错误、网络错误与 SDK 超时映射为OBJECTSTORAGE_UNAVAILABLE_001;调用方的取消原样抛出。没有配额(由桶策略管理)。 - 健康项
objectstorage.s3.connection:启动时与条件写入探测并行同步探一次(S3 连不上时启动只等一个超时),之后按HealthProbeIntervalSeconds(默认 15)在后台重探并缓存结果;对HealthBucketHEAD(桶不存在、无权限或服务不可达都算失败),关闭条件写入且没配时列桶;失败或超时条目 Degraded(KERNEL_UNAVAILABLE_005,reason只写异常类型,完整异常进日志),应用仍在服务。条件写入的状态即时并入(数据conditionalWrites:confirmed/unconfirmed/unsupported/disabled):尚未确认时错误是最近一次探测失败的错误码(HEAD 成功也一样):连不上 Degraded,启动之后才被拒绝或缺桶 Failed(DENIED_002/NOTFOUND_001);启动之后才探明不认时条目 Failed(OBJECTSTORAGE_UNAVAILABLE_003)。Failed 的条目让应用聚合为 Degraded,应用仍在服务。 - 范围读取、分片与预签名(ADR-0049,同时提供
objectstorage.multipart@1与objectstorage.presign@1):范围读取经Range头,Content-Range给出实际范围与总长,起点越界(416)报OBJECTSTORAGE_VALIDATION_001;分片直接映射 S3 multipart,ListParts分页取全,不覆盖的发起与完成要求条件写入已确认(探测同时核对条件完成),完成先 HEAD 再带If-None-Match: *,服务端拒绝分片清单(InvalidPart、EntityTooSmall等)报OBJECTSTORAGE_VALIDATION_002,NoSuchUpload报OBJECTSTORAGE_NOTFOUND_002;预签名经 SDK 本地签出(签名 V4,端点是 http 时按 http 签),下载可带Content-Disposition,上传可限定内容类型,分片地址带上传标识与编号,客户端直连存储。 - 命名实例过出站地址策略(RFC-0008、ADR-0117):定义可能来自应用的库,端点不可信,
S3BlobStoreProvider建出的客户端只能连公网地址,自建存储要放行(XiHan:Security:Outbound)。被拒绝与连不上同样处理,读写返回OBJECTSTORAGE_UNAVAILABLE_001。缺省实例的端点是运维配的,不过策略。 - 命名实例(ADR-0050):特性总是登记 Provider
S3BlobStoreProvider(名s3)——缺省实例另选了文件系统、能力全部被顶替(经IShadowAwareFeature.ConfigureShadowed)或其余能力只是兜底选出时只登记它,不读本配置节与绑定、不建缺省客户端、不探测(ADR-0075);设置的键同本配置节(不含HealthProbeIntervalSeconds,探测间隔由注册表统一),未知键报错;每个实例按自己的端点、区域与密钥建客户端,不读绑定,ConditionalWrites开启时建出时探测条件写入(探明不认、被拒绝、探测桶不存在都不登记,注册表返回OBJECTSTORAGE_UNAVAILABLE_003/DENIED_002/NOTFOUND_001,下次取用重新探测;只有连不上的照常建出并在后台重试),实例被替换、移除或应用停止时停止重试、释放客户端(经公开构造函数建的S3BlobStore不探测、不拥有客户端);实例同时是IBlobMultipartStore与IBlobUrlSigner;健康探测对实例的HealthBucketHEAD(没配时列桶),连通之后再看条件写入是否已确认,尚未确认或不认时 Degraded。 - 缺省实例的一致性(ADR-0075、ADR-0082):缺省的
IBlobStore、IBlobMultipartStore、IBlobUrlSigner必须落在同一存储上,只约束被要求的契约(有特性依赖,或XiHan:Providers/UseProvider指定)。只把objectstorage.blob指定给文件系统时,本包的分片上传与预签名只是兜底选出,本包按被顶替处理(只登记命名实例 Provider,不读配置节与绑定、不建客户端、不探测),不必装HostedBlobUrlFeature;有特性依赖分片上传或预签名、或配置把它们指定给本包时,它们是被要求的,与缺省对象存储不一致即OBJECTSTORAGE_VALIDATION_004(BlobStoreSelection.ProvidesDefaultInstance)启动失败——这时把预签名指定给xihan.hosting.blob-urls、分片上传指定给文件系统。 - 不满足 AOT 分析器(AWS SDK 的请求编组基于运行时反射)。
依赖方向
依赖 XiHan.Framework.ObjectStorage.Abstractions 与 AWSSDK.S3。
测试
ObjectStorage.S3.Tests 在设置环境变量 XIHAN_TEST_S3(服务端点,如 http://localhost:9000)时对真实服务跑 ObjectStorage.Conformance 全部用例(含命名实例的 BlobStoreProviderConformance)与条件写入探测的用例(探测桶 xihan-probe 由用例建出),XIHAN_TEST_S3_ACCESS_KEY / XIHAN_TEST_S3_SECRET_KEY 缺省为 rustfsadmin;未设置时跳过。XIHAN_TEST_S3_UNCONDITIONAL 指向不认条件写入的兼容服务(如 docker run -p 8000:8000 -e SCALITY_ACCESS_KEY_ID=rustfsadmin -e SCALITY_SECRET_ACCESS_KEY=rustfsadmin -e REMOTE_MANAGEMENT_DISABLE=1 zenko/cloudserver:8.2.7)时,核对它让启动与命名实例的建出都以 OBJECTSTORAGE_UNAVAILABLE_003 失败;指向 RustFS 时另核对密钥错、探测桶不存在让启动与命名实例的建出失败(ADR-0093)。探测的判定、失败原因、超时与关闭条件写入,启动时被拒绝、缺桶即启动失败,以及启动时连不上的照常启动、后台退避重试、写入时就地探测与恢复、之后才探明不认或才被拒绝 / 缺桶(ADR-0090、ADR-0093),由内存替身 FakeS3Client 加 FakeTimeProvider 覆盖,不连服务、不靠真实等待。本地用 docker run -p 9000:9000 rustfs/rustfs:1.0.4 起一个 S3 兼容的 RustFS 即可(不设 RUSTFS_ACCESS_KEY / RUSTFS_SECRET_KEY 时根凭据就是 rustfsadmin;MinIO 社区版已停发镜像,选型见 ADR-0086)。
状态
P5:可用,通过 ObjectStorage.Conformance(RustFS 1.0.4、MinIO)。
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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. |
-
net10.0
- AWSSDK.S3 (>= 4.0.104.8)
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.14)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.14)
- Microsoft.Extensions.Diagnostics.Abstractions (>= 10.0.14)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.14)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.14)
- Microsoft.Extensions.Options (>= 10.0.14)
- System.Threading.RateLimiting (>= 10.0.14)
- XiHan.Framework.ObjectStorage.Abstractions (>= 5.0.0-preview.1)
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 |
|---|---|---|
| 5.0.0-preview.2 | 44 | 11/6/2026 |
| 5.0.0-preview.1 | 45 | 11/4/2026 |