上一篇把对象的存储类型显示了出来——你能看到某个对象在标准、低频还是归档层。但只看得见不够,真正省钱要能动手:把冷数据转到归档层,以及在需要时把归档对象取回解冻。这次补上这两个写操作。
- 转换存储类型:右键对象 →「转换存储类型」→ 按账号所属厂商列出可选层级(标准 / 低频 / 归档 / 深度归档…)→ 一键转换。
- 取回归档:归档 / 冷归档的对象不能直接下载,得先发一个取回请求、等它解冻。右键 →「取回归档」→ 填保持天数。
两个操作都是写请求、都得跨 7 家云,而且——没法在本地做端到端测试(要连真实云)。所以关键是尽量复用已经验证过的代码路径,而不是新写一堆签名逻辑。
转换:一次"复制到自己"
对象存储没有"改一下这个对象的存储类型"这种直接操作。但它们都支持服务端复制时指定目标存储类型。所以转换的实现是:把对象复制到它自己,带上新的存储类型头和"元数据保持不变"指令:
// s3-core:PUT 到同一个 key,带三个头
amz_headers: &[
("x-amz-copy-source", format!("/{bucket}/{key}")),
("x-amz-storage-class", class), // 新存储类型
("x-amz-metadata-directive", "COPY"), // 元数据原样保留
],
这条路复用的正是各 SDK 已经在用的 copy_object 签名逻辑——我只是在被签名的头里多加了两个。阿里云用 x-oss-storage-class、腾讯云 x-cos-storage-class、华为云 x-obs-storage-class,头名不同,但套路一模一样。
取回:POST ?restore,复用分片上传的子资源签名
取回是一个带子资源的 POST:POST /{bucket}/{key}?restore,请求体写明保持天数:
<RestoreRequest><Days>2</Days></RestoreRequest>
难点在签名。?restore 是一个子资源,必须计入签名的规范化资源里,否则签名不过。自己手写子资源签名容易出错,而且没法连真云验证。
但这里有个现成的宝贝:分片上传早就在正确地签子资源了。初始化分片上传是 POST ?uploads、上传分片带 ?partNumber&uploadId——这些子资源 POST 一直跑得好好的。所以我没有另起炉灶,而是直接复用各 SDK 里那个已经验证过的"子资源请求"构造器:
// 阿里云 / 华为:复用 build_part_request,只是把子资源换成 restore
self.build_part_request(bucket, PartRequest {
method: Method::POST,
key,
subresources: &[("restore", None)], // ← 和 ("uploads", None) 同一条路
content_type: Some("application/xml"),
body: Some(restore_body),
..
})
restore 和 uploads 走的是完全相同的签名代码。这样取回的签名正确性,搭的是分片上传那条久经考验的便车,而不是新写的、没法测的逻辑。
对每家云:trait 默认兜底
老规矩,这两个操作是 trait 上的新方法,默认返回"不支持",支持的适配层覆盖并置上 storage_class_ops 能力位。7 家全实现了;以后新接入的云,不实现也只是这两项不可用(安全回退),实现了就自动拥有。
诚实说一句:测试的边界
这次我要坦白测试的局限。转换和取回是写操作,真正的行为(存储类型有没有变、取回有没有发起)只能连真实云验证,没法在 cargo test 里断言。我能测的、也测了的是:trait 默认在未实现时安全报错而非 panic;签名逻辑本身则通过复用各 SDK 已有的、已测的 copy / 分片子资源路径来获得信心——没有新写一行签名代码。
所以这一版:请求构造是复用可信路径搭出来的,端到端行为待真实环境确认。把这条边界讲清楚,比假装"全绿=一定对"更诚实。
小结
存储类型从"看得见"走到了"管得了":转换 = 带存储类型头的自我复制,取回 = 复用分片子资源签名的 POST ?restore,两者都建在各 SDK 已验证的代码路径上,靠 trait 默认覆盖每家云。冷数据转归档省钱,要用时再取回——闭环补上了。