01 Rust 大文件断点上传下载入门:用 rusty-cat 让上传下载更可靠

适合读者:第一次听说 rusty-cat、刚开始学习 Rust 异步编程、想在项目里做大文件上传下载但不知道从哪里下手的同学。

1. 先说痛点:为什么大文件传输不能只用一次 HTTP 请求?

很多小白第一次做文件上传下载时,最容易写出这样的逻辑:

rust 复制代码
// 伪代码:一次性把整个文件发出去
let bytes = tokio::fs::read("video.mp4").await?;
client.post(url).body(bytes).send().await?;

这个写法在小文件上看起来没问题,但一旦文件变大,就会暴露很多问题:

  • 文件太大时,一次性读入内存会占用大量 RAM。
  • 网络中断后,只能从头再传,用户体验很差。
  • 传输进度不好管理,界面上很难显示准确进度。
  • 多个文件同时上传下载时,需要自己写队列、并发控制、取消、暂停、恢复。
  • 对接阿里云 OSS、Azure Blob 这类对象存储时,还要处理分片、签名、合并、Range 下载等细节。

rusty-cat 要解决的就是这类问题。它是一个 Rust 异步文件传输 SDK,核心能力是:把大文件拆成多个分片,放到后台调度器中执行,并通过回调告诉业务层当前进度和状态。

一句话介绍:

rusty-cat 是一个基于 Tokio 的 Rust 异步 SDK,用来做可恢复的大文件上传和下载,支持普通 HTTP、阿里云 OSS、阿里云预签名 URL、Azure Blob、Azure SAS URL 等接入方式。

项目地址:https://github.com/0barman/rusty-cat

2. rusty-cat 适合解决什么问题?

它最适合这些场景:

  • 桌面客户端上传大文件到后端或对象存储。
  • CLI 工具下载大模型、安装包、日志归档。
  • 后台服务把本地文件上传到阿里云 OSS 或 Azure Blob。
  • IM、网盘、素材管理、备份工具等需要显示传输进度的应用。
  • 需要暂停、恢复、取消任务的文件传输系统。
  • 业务层有自己的数据库,希望 SDK 只负责传输,不强行绑定某个数据库。

它不适合这些场景:

  • 跨进程消息队列,例如 Kafka、RabbitMQ 那类系统。
  • 超高频低延迟交易系统。
  • 浏览器内直接运行的 WASM 文件传输。
  • 需要 SDK 内置数据库、账号系统、权限系统的一站式产品。

rusty-cat 的定位比较清晰:它不是"网盘系统",而是"文件传输执行层"。

3. 安装 rusty-cat

在你的 Rust 项目里加入依赖:

toml 复制代码
[dependencies]
rusty-cat = "0.2.2"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }

说明一下:

  • crate 名是 rusty-cat,但 Rust 代码里导入时要写 rusty_cat,因为 Rust 模块名不能直接使用连字符。
  • 本文示例以 0.2.2 API 为基础;正式接入时建议先查看 https://crates.io/crates/rusty-cathttps://docs.rs/rusty-cat,确认依赖版本仍为当前最新稳定版本。
  • 如果你使用的是更新版本,请以对应版本的 docs.rs 文档为准核对 API 名称和 feature flag。
  • 推荐统一从 rusty_cat::api::* 导入常用类型,这样对小白更友好。

最常用导入方式:

rust 复制代码
use rusty_cat::api::*;

4. 30 秒看懂最小下载 Demo

下面这个例子演示一个 HTTP 断点下载任务。它创建配置、创建客户端、构造下载任务、提交任务并等待完成。

rust 复制代码
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 task = DownloadPounceBuilder::new(
        "demo.bin",
        "./downloads/demo.bin",
        1024 * 1024,
        "https://example.com/demo.bin",
    )
    .build();

    let outcome = client
        .enqueue_and_wait(task, |record| {
            println!(
                "文件={} 进度={:.2}% 状态={:?}",
                record.file_name(),
                record.progress() * 100.0,
                record.status(),
            );
        })
        .await?;

    println!("任务完成:task_id={} payload={:?}", outcome.task_id, outcome.payload);

    client.close().await?;
    Ok(())
}

注意:上面的 https://example.com/demo.bin 只是占位地址。真正运行时,请替换成一个真实可访问、并且支持 HEADRange GET 的下载地址;否则示例会因为远程服务不支持断点下载或文件不存在而失败。

这里小白最需要理解 4 个对象:

  • MeowConfig:SDK 配置,例如并发数、HTTP 超时、队列容量。
  • MeowClient:SDK 的主入口,用来提交、暂停、恢复、取消任务。
  • DownloadPounceBuilder:下载任务构造器,用来填写文件名、保存路径、分片大小和下载 URL。
  • FileTransferRecord:进度回调里的状态快照,里面有任务 ID、文件名、总大小、进度、状态等信息。

5. 下载 Demo 每个参数是什么意思?

这行代码最重要:

rust 复制代码
let task = DownloadPounceBuilder::new(
    "demo.bin",
    "./downloads/demo.bin",
    1024 * 1024,
    "https://example.com/demo.bin",
)
.build();

逐个解释:

参数 示例 作用
file_name "demo.bin" 展示用文件名,会出现在日志和进度回调里。
file_path "./downloads/demo.bin" 下载到本地的目标路径。
chunk_size 1024 * 1024 每个分片大小,单位是字节。这里是 1 MiB。传 0 时 SDK 会默认使用 1 MiB。
url "https://example.com/demo.bin" 远程下载地址。普通 HTTP 下载要求服务端支持 HEADRange

下载时,SDK 会先用 HEAD 获取文件大小,再用带 Range 请求头的 GET 分片下载。例如一个 10 MiB 的文件,如果 chunk_size = 1 MiB,理论上会拆成 10 个分片。

为什么推荐 1 MiB 到 8 MiB?

  • 太小:HTTP 请求太多,浪费网络和服务端资源。
  • 太大:失败后重试成本高,进度更新也不够细。
  • 1 MiB 到 8 MiB 对多数对象存储和普通 HTTP 服务是比较容易接受的起点。

6. 最小上传 Demo

上传任务使用 UploadPounceBuilder。如果是普通 HTTP 上传,默认协议会按 multipart/form-data 形式上传分片。

rust 复制代码
use rusty_cat::api::{MeowClient, MeowConfig, UploadPounceBuilder};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let client = MeowClient::new(MeowConfig::default());

    let task = UploadPounceBuilder::new(
        "demo.bin",
        "./demo.bin",
        1024 * 1024,
    )
    .with_url("https://upload.example.com/files")
    .with_max_chunk_retries(3)
    .build()?;

    let outcome = client
        .enqueue_and_wait(task, |record| {
            println!(
                "上传进度={:.2}% 状态={:?}",
                record.progress() * 100.0,
                record.status(),
            );
        })
        .await?;

    println!("上传完成:task_id={} payload={:?}", outcome.task_id, outcome.payload);
    client.close().await?;
    Ok(())
}

注意:上面的 https://upload.example.com/files 也是占位上传接口。真实运行前,你需要准备一个能够接收分片 multipart/form-data 请求的后端接口,或者改用阿里云 OSS、Azure Blob、预签名 URL 等 provider 插件。

上传参数解释:

参数或方法 作用
UploadPounceBuilder::new(file_name, file_path, chunk_size) 创建基于本地文件路径的上传任务。
file_name 展示用文件名,也会进入默认 multipart 表单字段。
file_path 本地源文件路径。build() 时会读取文件 metadata,所以路径不存在会报 std::io::Error
chunk_size 分片大小。传 0 会变成默认 1 MiB。
with_url(url) 设置上传目标 URL。普通 HTTP 上传一般是后端接口地址;OSS/Azure 直传通常是对象最终 URL。
with_max_chunk_retries(3) 每个分片失败后最多额外重试 3 次。传 0 表示不重试。
build() 生成真正提交给 SDK 的 PounceTask

7. 进度回调里能拿到什么?

enqueue_and_waittry_enqueue 都会接收进度回调。回调参数是 FileTransferRecord

常用方法:

方法 含义
record.task_id() 当前任务 ID,后续暂停、恢复、取消都需要它。
record.file_sign() 文件签名。上传通常由 SDK 计算,下载可以通过业务参数设置。
record.file_name() 文件展示名。
record.total_size() 文件总大小,单位字节。
record.progress() 进度比例,范围通常是 0.0..=1.0。显示百分比时乘以 100。
record.status() 当前状态,例如 PendingTransmissionComplete
record.direction() 方向,上传或下载。

常见状态:

状态 说明
None 初始占位状态。
Pending 已进入队列,等待执行。
Transmission 正在传输。
Paused 已暂停。
Complete 成功完成。
Failed(err) 失败,并携带错误。
Canceled 已取消。

新手注意:回调里不要做很慢的事情。比如不要在每次进度更新时都同步写数据库。更好的做法是把记录发送到你自己的队列,由后台任务批量写入。

8. enqueue_and_waittry_enqueue 有什么区别?

小白建议先用 enqueue_and_wait

rust 复制代码
let outcome = client.enqueue_and_wait(task, |record| {
    println!("progress={}", record.progress());
}).await?;

它会一直等待任务进入最终状态:

  • 成功:返回 Ok(TaskOutcome)
  • 失败:返回 Err(MeowError)
  • 取消:返回 TaskCanceled 相关错误。

如果你正在做 GUI、服务端任务队列、批量上传下载,更常用的是 try_enqueue

rust 复制代码
let task_id = client
    .try_enqueue(
        task,
        |record| {
            println!("progress={:.2}%", record.progress() * 100.0);
        },
        |task_id, payload| {
            println!("任务 {task_id} 完成:{payload:?}");
        },
    )
    .await?;

它只负责"提交任务",不会等完整传输结束。返回的 TaskId 要保存起来,用于暂停、恢复、取消。

try_enqueue 名字里的 try_ 很重要:它采用快速失败的背压语义。如果命令队列瞬间满了,它会返回错误,而不是一直等待。大量任务同时入队时,可以调大 command_queue_capacity,或者在业务层做重试和限速。

9. 为什么 rusty-cat 比手写 reqwest 更省心?

如果你直接用 reqwest,需要自己处理:

  • 文件切片读取。
  • HTTP Range 下载。
  • 上传分片请求。
  • 每个分片失败重试。
  • 上传前 prepare。
  • 上传完成 complete。
  • 任务状态机。
  • 进度回调。
  • 任务暂停、恢复、取消。
  • 多任务并发控制。
  • 阿里云 OSS 或 Azure Blob 的签名和分片协议。

rusty-cat 把这些拆成几个清晰层次:

层次 负责什么
MeowClient SDK 入口、提交任务、生命周期控制。
MeowConfig 并发、队列、HTTP 超时等全局配置。
UploadPounceBuilder / DownloadPounceBuilder 把业务参数组装成任务。
后台调度器 分片调度、重试、状态变化、回调分发。
BreakpointUpload / BreakpointDownload 自定义上传/下载协议,例如 OSS、Azure、预签名 URL。

也就是说,你不需要一上来就研究内部调度器。新手先会用 Builder + Client,就能完成大多数接入。

10. 和常见方案简单对比

方案 优点 不足
直接 reqwest 上传下载 灵活、依赖少 断点续传、重试、进度、暂停恢复都要自己写。
tokio::fs + 手写 Range 可控性强 很容易写出复杂状态机,新手维护成本高。
云厂商官方 SDK 云能力完整 不一定统一上传下载生命周期,也不一定适配你的本地任务队列。
rusty-cat 聚焦大文件断点上传下载,统一进度和生命周期 不是数据库、不是权限系统,业务持久化需要你自己做。

11. 安全性和使用边界

rusty-cat 的一个重要设计是:不内置数据库,也不持久化你的云密钥。

这对小白来说可能一开始不太习惯,但它有好处:

  • SDK 不决定你用 SQLite、PostgreSQL、Redis 还是别的数据库。
  • SDK 不把 AccessKey、Account Key、SAS URL 等敏感数据写进本地文件。
  • 你可以把业务记录、权限、用户 ID、凭证引用放在自己的系统里。
  • 进程重启后,你可以从自己的数据库读取未完成任务,再重新构造任务入队。

如果是桌面端、移动端、公开客户端,不建议把阿里云 AccessKey Secret 或 Azure Account Key 放到客户端。更安全的方式是让后端生成短期有效的预签名 URL 或 SAS URL。

12. 本篇小结

rusty-cat 的核心价值不是"帮你发一个 HTTP 请求",而是把大文件传输中最容易写乱的部分统一起来:

  • 分片。
  • 断点续传。
  • 失败重试。
  • 进度回调。
  • 后台调度。
  • 暂停、恢复、取消。
  • 普通 HTTP、阿里云 OSS、Azure Blob、多种预签名模式。

如果你是 Rust 新手,可以先从 MeowConfig::default()MeowClient::new(...)DownloadPounceBuilderUploadPounceBuilder 开始。等你理解了任务和回调,再进入下一篇,系统学习每个配置参数应该怎么填。

相关推荐
月落归舟2 小时前
深入理解Cookie与Session:解决HTTP无状态的核心方案
网络·网络协议·http
zyk_computer12 小时前
AI 时代,或许 Rust 比 Python 更合适
人工智能·后端·python·ai·rust·ai编程·vibe coding
Rust语言中文社区14 小时前
【Rust日报】2026-05-14 Pyrefly v1.0 正式发布:快速的 Python 类型检查器和语言服务器
开发语言·后端·python·rust
不可食用盐16 小时前
# AI开发基于 Tauri 2 + React 的所见即所得 Markdown 编辑器
react.js·rust·ai编程
菜_小_白1 天前
tcpdump
linux·网络·测试工具·http·tcpdump
Simon523141 天前
HTTP、Cookie、Session知识小计
网络·网络协议·http
XS0301061 天前
HTTP协议
网络·网络协议·http
星栈1 天前
每次改订单,我都存了快照
后端·rust·开源
仍然.1 天前
HTTPS
网络协议·http·https