rusty-cat × 阿里云 OSS 直传直下实战(AccessKey 版)
本文是 rusty-cat 分片并发文档 的「云存储实战」系列之一,教你用 AccessKey 直连 的方式,把大文件分片并发上传到阿里云 OSS、并从 OSS 下载。 先读概念请看:《分片并发上传指南》、《分片并发下载指南》。 需要的 feature:
aliyun-oss-direct。
1. 适用场景与密钥模型(先想清楚安全)
「直传(direct)」= 客户端进程直接持有 OSS 的 AccessKeyId / AccessKeySecret,自己在本地做 OSS V4 签名。
🖼️ 【配图占位 1|direct 密钥模型】 建议配图:一台「可信服务器」图标里画一把钥匙(AccessKey),箭头直接连到 OSS;旁注「密钥在客户端」。与之对照可再画一个打叉的「用户手机/浏览器」,强调这类不可信环境不要用 direct。 临时 ASCII 草图:
css[可信服务器 🔑AccessKey] ──直接签名请求──▶ [阿里云 OSS] [用户手机/浏览器 ❌] 不要把 AccessKey 放这里 → 请改用「预签名」
- ✅ 适合 :程序跑在你完全掌控的可信服务器 / 后端上。
- ❌ 不适合 :手机 App、桌面客户端、浏览器 WASM 等不可信环境------那些场景请改用 《阿里云 OSS 预签名实战》,密钥永远留在后端。
2. 加依赖
toml
[dependencies]
rusty-cat = { version = "0.2.4", features = ["aliyun-oss-direct"] }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
3. 上传:完整可运行程序
把顶部常量换成你自己的,cargo run 即可。with_max_parts_in_flight(4) 就是「单文件 4 个分片并发上传」的开关。
rust
use std::sync::Arc;
use rusty_cat::aliyun_oss_direct::AliOssDirectUpload;
use rusty_cat::api::{FileTransferRecord, MeowClient, MeowConfig, UploadPounceBuilder};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let client = MeowClient::new(MeowConfig::default());
// Bucket、AccessKeyId、AccessKeySecret、地域
let protocol = AliOssDirectUpload::new("my-bucket", "AK_ID", "AK_SECRET", "cn-beijing");
let task = UploadPounceBuilder::new(
"big-object.bin", // 展示用文件名
"./big-object.bin", // 本地文件路径
1024 * 1024, // 分片大小 = 1 MiB
)
.with_url("https://my-bucket.oss-cn-beijing.aliyuncs.com/test/big-object.bin")
.with_breakpoint_upload(Arc::new(protocol)) // 协议对象必须放进 Arc
.with_max_parts_in_flight(4) // 单文件最多 4 片并发
.with_max_chunk_retries(3) // 每片失败最多再重试 3 次
.build()?;
let outcome = client
.enqueue_and_wait(task, |r: FileTransferRecord| {
println!("上传进度 {:.1}% 状态 {:?}", r.progress() * 100.0, r.status());
})
.await?;
println!("上传完成!服务端返回={:?}", outcome.payload);
client.close().await?; // 必须调用
Ok(())
}
内部发生了什么
SDK 会自动帮你走完整个 OSS Multipart 流程:
🖼️ 【配图占位 2|OSS 并发分片上传数据流】 建议配图:客户端先向 OSS 发一次
InitiateMultipartUpload打开会话;随后 4 条并行 箭头表示同时在发的UploadPart(分片 1~4,每片的分片号 = 偏移 / 分片大小 + 1);全部到达后,客户端发一次CompleteMultipartUpload(带x-oss-complete-all: yes)让 OSS 按序合并。图注强调:Complete 只执行一次,且必须在所有分片落地、拼成连续前缀之后。 临时 ASCII 草图:
python客户端 ──InitiateMultipartUpload──▶ OSS(打开会话,拿到 UploadId) ═══UploadPart #1═══▶ ┐ ═══UploadPart #2═══▶ ├─ 最多 4 片并行 ═══UploadPart #3═══▶ │ ═══UploadPart #4═══▶ ┘ ──CompleteMultipartUpload(x-oss-complete-all)──▶ OSS 按序合并 ✅
- 每片的分片号由偏移量推导,重传幂等,所以乱序 / 并发是安全的;
- OSS 最多 10000 个分片,SDK 会提前校验;
- 分片大小建议 1--8 MiB ,峰值内存 ≈
n × 分片大小(这里 4 × 1 MiB = 4 MiB)。
4. 下载:两种方式
方式一(串行):用 direct 下载协议
挂上 AliOssDirectDownload,SDK 用 AccessKey 签名去拉对象。注意 direct 下载协议只能串行 (with_max_parts_in_flight 对它无效)。
rust
use std::sync::Arc;
use rusty_cat::aliyun_oss_direct::AliOssDirectDownload;
use rusty_cat::api::{DownloadPounceBuilder, MeowClient, MeowConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let client = MeowClient::new(MeowConfig::default());
let protocol = AliOssDirectDownload::new("my-bucket", "AK_ID", "AK_SECRET", "cn-beijing");
let task = DownloadPounceBuilder::new(
"obj.bin",
"./downloads/obj.bin", // 确保目录已存在
1024 * 1024,
"https://my-bucket.oss-cn-beijing.aliyuncs.com/test/obj.bin",
)
.with_breakpoint_download(Arc::new(protocol)) // 挂 provider 协议 = 串行
.build();
client.enqueue_and_wait(task, |_r| {}).await?;
client.close().await?;
Ok(())
}
方式二(推荐,可并发):签名 GET URL + 默认下载器
想要并发 下载 OSS 对象,就不要 挂 provider 下载协议,而是让后端签一个预签名 GET URL,直接喂给内置的默认下载器:
rust
use rusty_cat::api::{DownloadPounceBuilder, MeowClient, MeowConfig};
// presigned_get_url:后端签发的 OSS 预签名 GET URL
// total_size:文件总字节数(从你的元数据接口拿)
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let presigned_get_url = "https://my-bucket.oss-cn-beijing.aliyuncs.com/test/obj.bin?Expires=...&Signature=...";
let total_size: u64 = 20 * 1024 * 1024;
let client = MeowClient::new(MeowConfig::default());
let task = DownloadPounceBuilder::new("obj.bin", "./downloads/obj.bin", 2 * 1024 * 1024, presigned_get_url)
.with_total_size(total_size) // 告诉 SDK 总大小,跳过 HEAD
.with_max_parts_in_flight(4) // 4 个 Range GET 并发
// 不要 with_breakpoint_download(...),保持默认 StandardRangeDownload
.build();
client.enqueue_and_wait(task, |r| println!("{:.1}%", r.progress() * 100.0)).await?;
client.close().await?;
Ok(())
}
这就是「上传下载不对称」的关键点:上传要挂 provider 协议才并发,下载要不挂 provider 协议才并发 。并发下载的完整规则(稳定 URL、
.rcdl续传、InvalidRange等)见 《分片并发下载指南》第 5~8 节。签 URL 的细节见 《阿里云 OSS 预签名实战》。
5. 断点续传与取消
- 续传 :进程重启后重建同一个上传任务再
try_enqueue。direct 上传续传时,SDK 通过ListMultipartUploads接管已有的 multipart 会话,并校验每个预期分片都在远端。建议持久化 provider 的UploadId(AliOssDirectUpload::current_upload_id()),方便带外中止孤儿会话。 - 给 Bucket 配一条
AbortMultipartUpload生命周期规则:让废弃的 multipart 会话自动清理,避免未提交分片一直占存储、计费。 - 取消上传 会触发
AbortMultipartUpload,让未提交分片停止占用存储。取消是尽力而为、且不可逆。
🖼️ 【配图占位 3|续传接管会话】 建议配图:时间轴上「第一次运行」传了分片 1
2 后进程被杀;「第二次运行」通过N。图注:已验证的连续前缀不重传。ListMultipartUploads找回同一个UploadId,只重传分片 3
6. 进度回调与任务控制(暂停 / 恢复 / 取消)
前面用的 enqueue_and_wait 会一直等到任务结束,适合脚本或一次性任务。做 UI、或要在后台并发跑并随时控制任务时,改用底层的 try_enqueue:它立刻返回 TaskId,让你分别处理进度回调和完成回调。把上文构建好的 task 传进去即可:
rust
use rusty_cat::api::{
FileTransferRecord, MeowClient, MeowError, PounceTask, TaskId, TransferStatus,
};
// task 按上文「上传」或「下载」小节构建好后传进来
async fn enqueue_with_control(
client: &MeowClient,
task: PounceTask,
) -> Result<TaskId, MeowError> {
let task_id = client
.try_enqueue(
task,
// 进度回调:每次进度更新都会调用
move |record: FileTransferRecord| match record.status() {
TransferStatus::Failed(err) => eprintln!("失败:{err}"),
TransferStatus::Canceled => println!("已取消"),
_ => println!("进度 {:.1}%", record.progress() * 100.0),
},
// 完成回调:成功到达 Complete 时调用一次
move |task_id, payload| println!("任务 {task_id} 完成 {payload:?}"),
)
.await?;
Ok(task_id)
}
拿到 TaskId 后就能随时控制任务:
rust,ignore
client.pause(task_id).await?; // 暂停
client.resume(task_id).await?; // 从断点继续(TaskId 不变)
client.cancel(task_id).await?; // 取消(取消上传会触发 OSS 的 AbortMultipartUpload 清理未提交分片)
record.status()返回&TransferStatus,取值:Pending/Transmission/Paused/Complete/Failed(err)/Canceled;进度是0.0~1.0的比例(f32)。完整语义见《分片并发上传指南》第 8 节与《分片并发下载指南》第 9 节。
7. 相关阅读
- 《分片并发上传指南》 --- 上传的通用机制、两个并发旋钮、避坑、错误码。
- 《分片并发下载指南》 --- 并发下载核心配方、
.rcdl续传。 - 《阿里云 OSS 预签名实战》 --- 密钥留后端的更安全方案。
- 文档总索引:doc/README.md。
本文基于
rusty-cat0.2.4 ,代码片段均经过cargo check --features all编译验证。