上一篇做了存储类型的转换和取回,但只能一次动一个对象。这在成本管理上其实没什么用——你不会一个一个地把文件转归档,省钱是按 GB / TB 算的:把"去年的日志"整个前缀转到归档层,或者把一整个归档目录取回来。所以这次把这两个操作扩展到整个文件夹。
- 右键文件夹 →「转换存储类型」→ 选目标层级 → 整个前缀下的对象全部转过去。
- 右键文件夹 →「取回归档」→ 填天数 → 整层的归档对象一并发起取回。
几乎没写新代码
有意思的是,这个"批量"版本几乎没有新逻辑。因为需要的两块积木都已经有了:
- 递归遍历一个前缀下的所有文件——做文件夹下载 / 迁移 / 删除时就写好了
walk_dir。 - 对单个对象转换 / 取回——上一篇刚做完。
批量版就是把这两块拼起来:遍历出所有文件,逐个调单对象操作,按文件数报进度。
pub async fn set_storage_class_folder(
&self, account: &str, root: &str, class: &str, progress: ProgressFn<'_>,
) -> Result<()> {
let files = walk_dir(&self.provider(account)?, root).await?.0;
let total = files.len() as u64;
for (i, file) in files.iter().enumerate() {
self.provider(account)?.set_storage_class(&file.path, class).await?;
progress((i + 1) as u64, total);
}
Ok(())
}
进度按文件数计,汇入底部的传输面板,和下载 / 迁移的任务并排显示。这是"把能力建在公共抽象上"的又一次复利:遍历写一次、单对象操作写一次,批量版几乎白送。
一个小决定:取回时忽略单个失败
转换和取回有个语义差别。转换存储类型对任何对象都合法(标准 → 归档、归档 → 标准都行)。但取回只对归档 / 冷归档对象有效——对一个本就是标准层的对象发取回,服务端会拒绝。
而一个文件夹里往往是混着的:有些归档了、有些没有。如果取回时遇到第一个"非归档对象被拒绝"就整体中止,那这个批量操作基本没法用。所以取回对单个对象的失败宽容——忽略掉继续处理下一个,尽力把整层里该取回的都取回:
// 非归档对象会被拒绝,忽略单个错误以便对整层尽力取回。
let _ = self.provider(account)?.restore(&file.path, days).await;
转换则相反,保持严格(单个失败即上报),因为转换失败通常意味着真有问题(权限、不支持的层级),不该被静默吞掉。同样是批量,一个宽容、一个严格,取决于操作的语义。
小结
批量转换 / 取回没有引入新机制,只是把递归遍历(文件夹功能留下的)和单对象存储类型操作(上一篇留下的)拼在一起。成本管理终于能按前缀来做:整个目录转归档省钱,要用时再整层取回。附带一个语义细节——取回对单个失败宽容,转换保持严格。