# baidu-netdisk-sdk **Repository Path**: forkmess/baidu-netdisk-sdk ## Basic Information - **Project Name**: baidu-netdisk-sdk - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-07-16 - **Last Updated**: 2026-07-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 百度网盘 Rust SDK [English](README.md) | [中文](README_zh.md) 百度网盘开放平台 API 的 Rust SDK,提供文件管理、上传下载等功能。 ## 特性 - **API 覆盖**:文件管理、上传下载、媒体处理等 - **高性能**:支持并行上传、流式下载、多线程下载 - **优雅的错误处理**:分层的错误类型,提供中文错误描述 - **线程安全**:使用 `RwLock` 保证并发安全 - **灵活配置**:Builder 模式便于客户端配置 - **异步优先**:基于 `tokio` 异步运行时 ## 安装 添加到 `Cargo.toml`: ```toml [dependencies] baidu-netdisk-sdk = "0.1.6" tokio = { version = "1.0", features = ["full"] } ``` ## 快速开始 ### 1. 创建客户端 ```rust use baidu_netdisk_sdk::BaiduNetDiskClient; let client = BaiduNetDiskClient::builder() .app_key("your_app_key") .app_secret("your_app_secret") .build()?; ``` ### 2. 授权 ```rust use baidu_netdisk_sdk::BaiduNetDiskClient; // 获取设备码进行授权 let device_code = client.authorize().get_device_code().await?; println!("请访问: {}", device_code.verification_url); println!("输入用户码: {}", device_code.user_code); // 轮询获取访问令牌 let token = loop { match client.authorize().request_access_token(&device_code).await? { Some(t) => break t, None => tokio::time::sleep(std::time::Duration::from_secs(5)).await, } }; ``` ### 3. 文件操作 ```rust // 列出目录 let files = client.file().list_directory("/").await?; // 递归列出所有文件并分页 let result = client.file().list_all("/", 0, 100).await?; println!("找到 {} 个条目", result.list.len()); if result.has_more { // 使用 cursor 获取下一页 let next_result = client.file().list_all("/", result.cursor.unwrap() as i32, 100).await?; } // 搜索文件 let (results, has_more) = client.file().search_files("文档", "/").await?; // 上传文件 client.upload().upload_file("local/path.txt", "/remote/path.txt").await?; // 下载文件 client.download().download_single("/remote/file.txt", "./local/file.txt").await?; ``` ### 4. TokenScopedClient 多用户场景 对于需要同时使用多个令牌的多用户场景,可以创建 `TokenScopedClient`: ```rust // 创建绑定到特定令牌的作用域客户端 let scoped_client = client.with_token(token); // 使用作用域客户端 - 令牌会自动使用 let files = scoped_client.file().list_directory("/").await?; let quota = scoped_client.quota().get_quota().await?; // 每个作用域客户端都有独立的令牌上下文 // 多个作用域客户端可以并发使用而不会冲突 ``` **TokenScopedClient 的主要优势:** - **线程安全**:每个作用域客户端有独立的令牌上下文 - **隔离性**:修改一个作用域客户端不影响其他客户端 - **便捷性**:令牌由内部管理,无需显式传递 - **可缓存**:可以按用户缓存作用域客户端以提高性能 ## 配置 ### 日志 本 SDK 使用 `log` crate 进行日志记录。要查看日志输出,您需要在应用程序中初始化一个日志器。 **使用 `env_logger` 的示例:** ```toml # Cargo.toml [dependencies] baidu-netdisk-sdk = "0.1.6" env_logger = "0.11" tokio = { version = "1.0", features = ["full"] } ``` ```rust // main.rs fn main() { // 初始化日志器 env_logger::init(); // 现在 SDK 的日志将可见 let client = BaiduNetDiskClient::builder().build()?; // ... } ``` 通过环境变量控制日志级别: ```bash # 显示所有 info 级别的日志 RUST_LOG=info cargo run # 只显示此 SDK 的 debug 日志 RUST_LOG=baidu_netdisk_sdk=debug cargo run # 显示 trace 日志以进行详细调试 RUST_LOG=baidu_netdisk_sdk=trace cargo run ``` **可用的日志级别:** `error`, `warn`, `info`, `debug`, `trace` **注意**:如果您不初始化日志器,即使 SDK 内部生成了日志消息,也不会显示任何日志输出。 ### 环境变量 #### 构建器初始化时静默读取 这些变量在调用 `ClientBuilder::default()` 时(即 `BaiduNetDiskClient::builder()`)会自动读取。环境变量的值作为默认值,但可以通过显式的构建器调用覆盖。 | 变量 | 描述 | | ----------------------- | ----------------- | | `BD_NETDISK_APP_ID` | 应用 ID(可选) | | `BD_NETDISK_APP_KEY` | 应用 Key | | `BD_NETDISK_SECRET_KEY` | 应用 Secret | | `BD_NETDISK_APP_NAME` | 应用名称(可选,用于识别多个应用) | **注意**:如果您通过构建器显式设置这些值(例如 `.app_key("...")`),它们将覆盖任何环境变量的值。 #### 显式加载(手动读取) 这些变量在构建器初始化时不会自动读取。您必须显式调用 `load_token_from_env()` 才能加载它们。 | 变量 | 是否必需 | 描述 | | --------------------------- | -------- | ------------------------ | | `BD_NETDISK_ACCESS_TOKEN` | 是 | 访问令牌 | | `BD_NETDISK_REFRESH_TOKEN` | 是 | 刷新令牌 | | `BD_NETDISK_EXPIRES_IN` | 是 | 令牌过期秒数 | | `BD_NETDISK_SCOPE` | 否 | 权限范围(默认:"basic netdisk") | | `BD_NETDISK_SESSION_KEY` | 否 | 会话 Key | | `BD_NETDISK_SESSION_SECRET` | 否 | 会话 Secret | | `BD_NETDISK_ACQUIRED_AT` | 否 | 令牌获取时间戳(用于测试) | ### 避免重复配置 如果您想避免环境变量干扰,只需通过构建器显式设置所有必需的值: ```rust let client = BaiduNetDiskClient::builder() .app_key("your_app_key") // 显式设置,覆盖 BD_NETDISK_APP_KEY .app_secret("your_app_secret") // 显式设置,覆盖 BD_NETDISK_SECRET_KEY .build()?; // 令牌仍需显式加载或手动设置 client.load_token_from_env()?; // 加载令牌变量 // 或 client.set_access_token(manual_token)?; // 手动设置 ``` ### 客户端构建器选项 ```rust let client = BaiduNetDiskClient::builder() .app_key("your_app_key") // 应用 Key .app_secret("your_app_secret") // 应用 Secret .app_name("My App") // 应用名称(可选) .timeout(Duration::from_secs(30)) // 请求超时时间 .auto_refresh(true) // 自动刷新令牌 .refresh_ahead_seconds(86400) // 过期前24小时开始刷新 .max_retries(3) // 最大重试次数 .build()?; ``` ## API 模块 ### 文件管理 (`client.file()`) 核心文件操作: - `list_directory()` - 列出目录文件 - `list_all()` - 递归列出所有文件 - `get_file_info()` - 根据路径获取文件信息 - `get_file_meta()` - 根据 fs\_id 获取文件元信息(**包含下载链接 dlink**) - `search_files()` - 按关键词搜索文件 - `semantic_search()` - 语义搜索 - `create_folder()` - 创建目录 - `rename()` - 重命名文件/文件夹 - `move_file()` - 移动文件/文件夹 - `copy_file()` - 复制文件/文件夹 - `delete()` - 删除文件/文件夹 ### 下载 (`client.download()`) 下载方法: - `get_dlink_from_path()` - 根据文件路径获取下载链接 - `get_dlink_from_fsid()` - 根据 fs\_id 获取下载链接 - `auto_download()` - 根据文件大小自动选择最佳方法 - `auto_download_by_fsid()` - 通过 fs\_id 自动选择 - `download_single()` - 单线程下载(通过路径) - `download_single_by_fsid()` - 单线程下载(通过 fs\_id) - `download_single_with_meta()` - 单线程下载(使用 FileMeta) - `download_parallel()` - 多线程并行下载(通过路径) - `download_parallel_by_fsid()` - 多线程并行下载(通过 fs\_id) - `download_parallel_multi_threaded()` - 多线程并行下载(使用 FileMeta) - `download_streaming()` - 异步并发下载(通过路径) - `download_streaming_by_fsid()` - 异步并发下载(通过 fs\_id) - `download_streaming_with_meta()` - 流式下载(使用 FileMeta) ### 上传 (`client.upload()`) SDK 提供多种上传方法以适应不同场景: #### 上传方法对比 | 方法 | 数据来源 | 内存占用 | 流式 | 适用场景 | | ----------------------------------- | ------------- | ------ | -- | ---------- | | [`upload_file()`](#1-从文件路径上传) | 文件路径 | \~80MB | ✅ | 最常见的场景 | | [`upload_reader()`](#2-从-reader-上传) | Reader + 文件大小 | \~80MB | ✅ | 自定义读取器、包装流 | | [`upload_bytes()`](#3-从内存字节上传) | `&[u8]` 切片 | 全量数据 | ❌ | 内存中的小数据 | #### 核心特性 - **断点续传**:自动检测已上传的分片,只上传缺失的部分 - **并行上传**:支持多个分片同时上传(默认:10 并发) - **内存优化**:大文件内存占用受批量大小限制(默认约 80MB) - **自动分片**:文件自动切割为 4MB 分片 #### 1. 从文件路径上传 最简单的文件上传方式: ```rust use baidu_netdisk_sdk::BaiduNetDiskClient; let client = BaiduNetDiskClient::builder().build()?; client.load_token_from_env()?; // 简单上传 let response = client.upload().upload_file("local_file.txt", "/remote/file.txt").await?; println!("已上传: {} ({} 字节)", response.path, response.size); ``` 自定义选项上传: ```rust use baidu_netdisk_sdk::{BaiduNetDiskClient, upload::SimpleUploadOptions}; let options = SimpleUploadOptions::default() .chunk_size(8 * 1024 * 1024) // 8MB 分片 .max_concurrency(20); // 20 并发上传 let response = client.upload().upload_file_with_options("video.mp4", "/remote/video.mp4", options).await?; ``` #### 2. 从 Reader 上传 支持流式上传,适用需要自定义读取器的场景(需要 `Read + Seek`): ```rust use baidu_netdisk_sdk::BaiduNetDiskClient; use std::io::BufReader; let file = std::fs::File::open("local_file.txt")?; let metadata = file.metadata()?; let file_size = metadata.len(); let mut reader = BufReader::new(file); let response = client.upload() .upload_reader(&mut reader, file_size, "/remote/file.txt") .await?; ``` #### 3. 从内存字节上传 适用数据已经在内存中的场景: ```rust use baidu_netdisk_sdk::BaiduNetDiskClient; let data = b"Hello, World!"; let response = client.upload().upload_bytes(data, "/remote/hello.txt").await?; println!("已上传: {} 字节", response.size); ``` #### 断点续传原理 1. **第一次遍历**:读取文件 → 计算每个分片的 MD5 2. **预创建**:调用 API → 获取 uploadid 和已存在分片列表 3. **第二次遍历**:再次读取文件 → 只上传缺失的分片 4. **创建**:合并分片生成最终文件 这意味着如果上传中断,重启后只会上传缺失的分片。 ### 授权 (`client.authorize()`) - `get_device_code()` - 获取授权设备码 - `request_access_token()` - 轮询获取访问令牌 ### 用户与配额 - `client.user().get_user_info(vip_version)` - 获取用户信息 (vip_version: None 或 Some("v2")) - `client.quota().get_quota()` - 获取基本存储配额 - `client.quota().get_capacity(check_free, check_expire)` - 获取详细容量信息 - `client.quota().get_quota_with_expire()` - 获取带过期检查的配额信息 ### 播单 (`client.playlist()`) 播单和媒体功能: **播单操作:** - `get_playlist_list()` - 列出播单 - `get_playlist_file_list()` - 列出播单中的文件 **媒体播放:** - `get_media_play_info()` - 获取媒体播放信息(支持路径或 fs\_id) - `get_media_m3u8_content()` - 根据路径获取原始 m3u8 内容 **便捷方法(画质枚举):** - `get_video_m3u8()` - 使用 VideoQuality 获取视频 m3u8 - `get_video_m3u8_highest()` - 为给定 VIP 等级获取最高画质视频 m3u8 - `get_audio_m3u8()` - 使用 AudioQuality 获取音频 m3u8 - `get_audio_m3u8_default()` - 使用默认画质 (128K) 获取音频 m3u8 **转码状态检查:** - `fetch_m3u8()` - 从 URL 获取 m3u8 内容 - `is_media_fully_transcoded()` - 检查媒体是否完全转码 (#EXT-X-ENDLIST) **画质枚举:** - `VideoQuality` - 视频画质级别(480P、720P、1080P) - `AudioQuality` - 音频画质级别(MP3 128K) - 画质方法:`to_media_type()`, `highest_for_vip_level()`, `available_for_vip_level()` - 根据 VIP 等级自动选择最佳画质 ## 错误处理 ```rust use baidu_netdisk_sdk::{NetDiskError, NetDiskResult}; match result { Ok(value) => println!("成功: {:?}", value), Err(e) => { eprintln!("错误: {}", e); if e.is_auth_error() { // 处理认证错误 - 重新授权 } else if e.is_not_found_error() { // 处理文件不存在 } } } ``` ## 令牌管理 ```rust // 手动设置令牌 let token = AccessToken::new( "access_token_string".to_string(), "refresh_token_string".to_string(), 2592000, // expires_in "basic netdisk".to_string(), ); client.set_access_token(token)?; // 从环境变量加载令牌 let token = client.load_token_from_env()?; // 验证令牌状态 match client.validate_token() { Ok(TokenStatus::Valid) => println!("令牌有效"), Ok(TokenStatus::ExpiringSoon) => println!("令牌即将过期"), Ok(TokenStatus::Expired) => println!("令牌已过期"), Err(e) => eprintln!("错误: {}", e), } ``` ## 示例 运行示例: ```bash # 授权流程 cargo run --example auth_flow # 文件操作 cargo run --example file # 搜索 cargo run --example search # 上传 cargo run --example upload_file cargo run --example upload_bytes cargo run --example upload_reader cargo run --example upload_file_options # 下载 cargo run --example download # 下载对比 cargo run --example download_compare # 令牌测试 cargo run --example token_test # 用户信息 cargo run --example user_info # 配额信息 cargo run --example quota # 播单 cargo run --example playlist ``` ## 下载策略指南 本 SDK 提供多种下载策略以适应不同场景: ### 并发 vs 并行 **并发** (`download_streaming`): - 使用单线程(或线程池)上的异步任务 - 对多个小文件或网络是瓶颈时高效 - 内存开销更低 - 适用场景:下载多个小文件、内存受限环境 **并行** (`download_parallel`): - 使用真正的多线程和专用 OS 线程 - 大文件吞吐量更高(最大化网络带宽) - 内存使用更高(每个线程有自己的堆栈) - 适用场景:大文件 (>100MB)、追求最大速度 ### 何时使用哪种方式 | 场景 | 推荐方式 | | --------------- | --------------------------- | | 小文件 (<10MB) | `auto_download()` 或并发流式下载 | | 中等文件 (10-100MB) | `auto_download()` 会自动选择最佳方式 | | 大文件 (>100MB) | 多线程并行 | | 多个文件 | 并发流式下载 | | 内存受限 | 单线程或并发流式下载 | | 追求最大速度 | 多线程并行 | ### 快速参考 ```rust // 根据文件大小自动选择(大多数情况推荐) // - < 10MB: 单线程 // - > 10MB: futures 并发流式下载(无论CPU核心数量都能保持良好性能) // 注意: 如需最大速度,请手动使用 `download_parallel` client.download().auto_download("/remote/file.zip", "./local/file.zip").await?; // 大文件追求最大速度(推荐 6+ 核心) client.download().download_parallel("/remote/large.iso", "./local/large.iso", Some(8)).await?; // 多个小文件或核心受限(<= 4) client.download().download_streaming("/remote/small.txt", "./local/small.txt", 4).await?; ``` ### 不确定用哪个?运行对比测试! 如果不确定哪种下载方法最适合您的硬件,请运行对比测试: ```bash cargo run --example download_compare ``` 测试会要求您输入 CPU 核心数(例如 4、8、12),然后: 1. 使用 **流式(Futures/并发)** 下载 2. 使用 **并行(多线程)** 下载 3. 显示并排速度对比 根据结果来决定哪种方法最适合您的特定硬件! **测试的关键发现:** - 4 核心: Futures(流式)通常比并行快 1.5\~2 倍 - 6-8 核心: 两种方法性能相似 - 8+ 核心: 并行由于更好的多核利用稍微领先 ## 性能提示 1. **大文件上传**:使用 `upload_file()`,自动分片并行上传 2. **大文件下载**:使用 `download_parallel_multi_threaded()` 获得最大速度 3. **令牌刷新**:设置 `refresh_ahead_seconds` 匹配您的使用模式 ## 许可证 MIT 许可证 - 详见 [LICENSE](LICENSE) 文件。 ## 贡献 欢迎贡献!请提交 Pull Request。