对象存储都有存储类型(storage class / 存储层):热数据放标准层,读得少的放低频省钱,几乎不读的丢进归档层更便宜(代价是取回要先解冻)。用久了你一定会想知道:某个对象现在到底在哪一层?是不是已经归档、直接下载会不会失败?
这次先把这件事的读侧做了:对象详情里新增一行存储类型,一眼看清每个对象所在的层。
数据其实一直都在
有意思的是,实现这个功能几乎没碰网络请求——因为列举对象的响应里本来就带存储类型。S3 的 ListObjectsV2 每个对象都有 <StorageClass>,OSS / COS / OBS 的列举也一样。我们各家的 SDK 早就把它解析进了内部结构:
pub struct ObjectSummary {
pub key: String,
pub size: u64,
pub etag: String,
pub last_modified: String,
pub storage_class: String, // ← 一直在解析,只是没往上传
}
只是到了统一的 Entry 模型这一层,它被丢掉了——Entry 里没有这个字段。所以真正要做的,是给统一模型加一个可选字段,让适配层把已经解析好的值透传上来:
pub struct Entry {
// ...
pub storage_class: Option<String>,
}
七家适配层各加一句 .with_storage_class(obj.storage_class),数据就通了。没有存储层概念的后端(或某对象没返回),字段留空即可——空字符串归一化成 None,不用每家单独判空。
坑:每家的名字都不一样
统一显示时撞上一个小麻烦:同一个概念,每家云的字符串都不同。
- AWS S3:
STANDARD/STANDARD_IA/GLACIER/DEEP_ARCHIVE/INTELLIGENT_TIERING - 阿里云 OSS:
Standard/IA/Archive/ColdArchive - 腾讯云 COS:
STANDARD/STANDARD_IA/ARCHIVE/DEEP_ARCHIVE - 华为云 OBS:
STANDARD/WARM/COLD
直接把 DEEP_ARCHIVE、WARM 这种词甩给用户不友好。所以前端做一层友好标签映射:把常见值翻成"标准 / 低频访问 / 归档 / 深度归档 / 智能分层……“,没收录的原样显示(不丢信息),字段为空时默认"标准”。
const storageLabel = (sc: string | null): string =>
sc ? (STORAGE_LABELS[sc.toUpperCase()] ?? sc) : "标准";
接下来:从"看得见"到"管得了"
这一步是读——把存储类型显示出来。下一步是写:
- 转换存储类型:把对象从标准转到低频 / 归档以省钱(通过带存储类型头的服务端复制实现)。
- 归档取回(restore):归档 / 深度归档的对象不能直接下载,得先发一个解冻请求等它取回。
这两个是写操作,要按各家的请求头和 POST ?restore 接口逐一实现,是更大的一块。先把"看得见"做扎实——毕竟你得先知道对象在哪一层,才谈得上管理它。