Rust SFTP客户端库russh-sftp的使用,实现安全高效的文件传输与远程操作
Russh SFTP
SFTP子系统支持的服务器和客户端,用于Russh及更多!
该crate可以提供与任何能够提供子系统通道原始数据输入输出的兼容性。 根据版本3规范(最流行的)实现。
项目的主要思想是提供在任何级别与协议交互的实现。
示例
- 客户端示例
- 简单服务器
已实现的功能?
- 基本数据包
- 扩展数据包
- 文件属性简化
- 客户端
- 客户端示例
- 服务器端
- 简单服务器示例
- 扩展支持:
limits@openssh.com
、hardlink@openssh.com
、fsync@openssh.com
、statvfs@openssh.com
- 完整服务器示例
- 单元测试
- 工作流
采用者
- kty - Kubernetes的终端。
一些话
感谢@Eugeny(Russh的作者)的及时帮助和Russh API的最终确定
以下是基于内容提供的示例,整理出的完整Rust SFTP客户端使用示例:
use russh::{client, ChannelMsg};
use russh_keys::load_secret_key;
use russh_sftp::protocol::FileAttributes;
use std::io::Write;
use std::path::Path;
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 配置SSH客户端
let config = client::Config {
connection_timeout: Some(Duration::from_secs(10)),
..Default::default()
};
// 加载SSH密钥
let key = load_secret_key("path/to/private/key", None)?;
// 连接到SFTP服务器
let mut session = client::connect(
config,
("sftp.example.com", 22),
"username",
key,
).await?;
// 打开SFTP子系统
let mut channel = session.channel_open_session().await?;
channel.request_subsystem(true, "sftp").await?;
// 创建SFTP客户端
let sftp = russh_sftp::client::Client::new(channel.into_stream());
// 示例1: 列出远程目录
let files = sftp.readdir(Path::new("/remote/path")).await?;
for file in files {
println!("File: {:?}", file);
}
// 示例2: 上传文件
let local_path = Path::new("local_file.txt");
let remote_path = Path::new("/remote/path/remote_file.txt");
let mut local_file = std::fs::File::open(local_path)?;
let mut remote_file = sftp.create(remote_path).await?;
std::io::copy(&mut local_file, &mut remote_file)?;
// 示例3: 下载文件
let mut remote_file = sftp.open(remote_path).await?;
let mut local_file = std::fs::File::create("downloaded_file.txt")?;
std::io::copy(&mut remote_file, &mut local_file)?;
// 示例4: 创建目录
sftp.mkdir(Path::new("/remote/path/new_dir"), FileAttributes::default()).await?;
// 示例5: 删除文件
sftp.remove(Path::new("/remote/path/file_to_delete.txt")).await?;
// 关闭连接
session.disconnect(None, "").await?;
Ok(())
}
此示例展示了如何使用russh-sftp库进行基本的SFTP操作,包括连接服务器、文件传输和目录操作。代码中包含了必要的错误处理和异步操作,确保安全高效的文件传输。
以下是一个更完整的SFTP客户端示例,包含更多实用功能:
use russh::{client, ChannelMsg};
use russh_keys::load_secret_key;
use russh_sftp::protocol::{FileAttributes, OpenOptions, StatusCode};
use std::io::{Read, Write};
use std::path::Path;
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// SSH客户端配置
let config = client::Config {
connection_timeout: Some(Duration::from_secs(30)),
auth_timeout: Some(Duration::from_secs(15)),
..Default::default()
};
// 加载私钥文件
let key_path = "~/.ssh/id_rsa"; // 替换为实际私钥路径
let key = load_secret_key(key_path, None)?;
// 建立SSH连接
let mut session = client::connect(
config,
("sftp.server.com", 22), // 替换为实际SFTP服务器地址
"your_username", // 替换为实际用户名
key,
).await?;
// 打开会话通道并请求SFTP子系统
let mut channel = session.channel_open_session().await?;
channel.request_subsystem(true, "sftp").await?;
// 创建SFTP客户端实例
let sftp = russh_sftp::client::Client::new(channel.into_stream());
// 1. 获取当前工作目录
let current_dir = sftp.realpath(Path::new(".")).await?;
println!("当前工作目录: {:?}", current_dir);
// 2. 列出目录内容
println!("\n目录列表:");
let entries = sftp.readdir(Path::new("/")).await?;
for entry in entries {
println!(" {:?}", entry);
}
// 3. 创建新目录
let new_dir = Path::new("/test_directory");
match sftp.mkdir(new_dir, FileAttributes::default()).await {
Ok(_) => println!("\n目录创建成功: {:?}", new_dir),
Err(e) => println!("\n目录创建失败: {}", e),
}
// 4. 文件上传示例
let local_file = "local_test.txt";
let remote_file = Path::new("/test_directory/uploaded_file.txt");
// 创建本地测试文件
std::fs::write(local_file, "Hello SFTP from Rust!")?;
// 上传文件
let mut local_data = std::fs::File::open(local_file)?;
let mut remote_handle = sftp.create(remote_file).await?;
std::io::copy(&mut local_data, &mut remote_handle)?;
println!("\n文件上传成功: {:?}", remote_file);
// 5. 文件下载示例
let download_path = "downloaded_file.txt";
let mut remote_handle = sftp.open(remote_file).await?;
let mut local_handle = std::fs::File::create(download_path)?;
std::io::copy(&mut remote_handle, &mut local_handle)?;
println!("文件下载成功: {}", download_path);
// 6. 获取文件属性
let attrs = sftp.stat(remote_file).await?;
println!("\n文件属性: {:?}", attrs);
// 7. 重命名文件
let new_remote_path = Path::new("/test_directory/renamed_file.txt");
sftp.rename(remote_file, new_remote_path).await?;
println!("文件重命名成功");
// 8. 删除文件
sftp.remove(new_remote_path).await?;
println!("文件删除成功");
// 9. 删除目录
sftp.rmdir(new_dir).await?;
println!("目录删除成功");
// 关闭SFTP会话
session.disconnect(None, "SFTP会话结束").await?;
// 清理本地文件
std::fs::remove_file(local_file)?;
std::fs::remove_file(download_path)?;
println!("\nSFTP操作完成!");
Ok(())
}
此完整示例演示了SFTP客户端的各种常用操作,包括文件上传下载、目录管理、文件操作等,并包含了完整的错误处理和资源清理。
1 回复
Rust SFTP客户端库 russh-sftp 使用指南
概述
russh-sftp 是一个基于 Rust 语言开发的 SFTP(SSH File Transfer Protocol)客户端库,提供安全高效的文件传输和远程操作功能。该库构建在 russh SSH 库之上,支持完整的 SFTP 协议操作。
主要特性
- 安全的 SSH 连接和身份验证
- 完整的 SFTP 操作支持
- 异步/同步文件传输
- 目录操作和管理
- 文件权限和属性管理
- 高性能的并发传输
安装方法
在 Cargo.toml 中添加依赖:
[dependencies]
russh-sftp = "0.4"
tokio = { version = "1.0", features = ["full"] }
基本使用方法
1. 建立 SFTP 连接
use russh_sftp::client::{SftpClient, SftpOptions};
use russh_sftp::protocol::FileOpenOptions;
use tokio::net::TcpStream;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 建立 TCP 连接
let stream = TcpStream::connect("example.com:22").await?;
// 创建 SFTP 客户端
let mut client = SftpClient::new(
stream,
SftpOptions::default()
.username("your_username")
.password("your_password")
).await?;
// 执行操作...
Ok(())
}
2. 文件上传示例
async fn upload_file(client: &mut SftpClient, local_path: &str, remote_path: &str) -> Result<(), Box<dyn std::error::Error>> {
// 打开本地文件
let mut file = tokio::fs::File::open(local_path).await?;
// 创建远程文件
let remote_file = client
.open(
remote_path,
FileOpenOptions::new()
.write(true)
.create(true)
.truncate(true),
)
.await?;
// 读取本地文件内容并写入远程文件
let mut contents = Vec::new();
file.read_to_end(&mut contents).await?;
remote_file.write_all(&contents).await?;
println!("文件上传成功: {}", remote_path);
Ok(())
}
3. 文件下载示例
async fn download_file(client: &mut SftpClient, remote_path: &str, local_path: &str) -> Result<(), Box<dyn std::error::Error>> {
// 打开远程文件
let mut remote_file = client
.open(remote_path, FileOpenOptions::new().read(true))
.await?;
// 创建本地文件
let mut local_file = tokio::fs::File::create(local_path).await?;
// 读取远程文件内容并写入本地文件
let mut contents = Vec::new();
remote_file.read_to_end(&mut contents).await?;
local_file.write_all(&contents).await?;
println!("文件下载成功: {}", local_path);
Ok(())
}
4. 目录操作示例
async fn list_directory(client: &mut SftpClient, path: &str) -> Result<(), Box<dyn std::error::Error>> {
// 列出目录内容
let entries = client.readdir(path).await?;
for entry in entries {
println!("名称: {}, 类型: {:?}, 大小: {} bytes",
entry.filename,
entry.attrs.file_type(),
entry.attrs.size.unwrap_or(0));
}
Ok(())
}
async fn create_directory(client: &mut SftpClient, path: &str) -> Result<(), Box<dyn std::error::Error>> {
client.mkdir(path, 0o755).await?;
println!("目录创建成功: {}", path);
Ok(())
}
5. 文件操作示例
async fn file_operations(client: &mut SftpClient) -> Result<(), Box<dyn std::error::Error>> {
// 重命名文件
client.rename("old_name.txt", "new_name.txt").await?;
// 删除文件
client.remove("file_to_delete.txt").await?;
// 获取文件属性
let attrs = client.stat("some_file.txt").await?;
println!("文件大小: {} bytes", attrs.size.unwrap_or(0));
Ok(())
}
错误处理
async fn handle_errors(client: &mut SftpClient) {
match client.stat("nonexistent_file.txt").await {
Ok(attrs) => println!("文件存在,大小: {}", attrs.size.unwrap_or(0)),
Err(russh_sftp::error::Error::Sftp(russh_sftp::protocol::Status::NoSuchFile)) => {
println!("文件不存在")
}
Err(e) => eprintln!("发生错误: {}", e),
}
}
完整示例代码
use russh_sftp::client::{SftpClient, SftpOptions};
use russh_sftp::protocol::FileOpenOptions;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 建立 SFTP 连接
let stream = TcpStream::connect("example.com:22").await?;
let mut client = SftpClient::new(
stream,
SftpOptions::default()
.username("your_username")
.password("your_password")
).await?;
// 创建测试目录
create_directory(&mut client, "test_dir").await?;
// 列出目录内容
list_directory(&mut client, ".").await?;
// 文件上传
upload_file(&mut client, "local_file.txt", "test_dir/remote_file.txt").await?;
// 文件下载
download_file(&mut client, "test_dir/remote_file.txt", "downloaded_file.txt").await?;
// 文件操作
file_operations(&mut client).await?;
// 错误处理示例
handle_errors(&mut client).await;
Ok(())
}
async fn upload_file(client: &mut SftpClient, local_path: &str, remote_path: &str) -> Result<(), Box<dyn std::error::Error>> {
let mut file = tokio::fs::File::open(local_path).await?;
let remote_file = client
.open(
remote_path,
FileOpenOptions::new()
.write(true)
.create(true)
.truncate(true),
)
.await?;
let mut contents = Vec::new();
file.read_to_end(&mut contents).await?;
remote_file.write_all(&contents).await?;
println!("文件上传成功: {}", remote_path);
Ok(())
}
async fn download_file(client: &mut SftpClient, remote_path: &str, local_path: &str) -> Result<(), Box<dyn std::error::Error>> {
let mut remote_file = client
.open(remote_path, FileOpenOptions::new().read(true))
.await?;
let mut local_file = tokio::fs::File::create(local_path).await?;
let mut contents = Vec::new();
remote_file.read_to_end(&mut contents).await?;
local_file.write_all(&contents).await?;
println!("文件下载成功: {}", local_path);
Ok(())
}
async fn list_directory(client: &mut SftpClient, path: &str) -> Result<(), Box<dyn std::error::Error>> {
let entries = client.readdir(path).await?;
println!("目录 {} 内容:", path);
for entry in entries {
println!("名称: {}, 类型: {:?}, 大小: {} bytes",
entry.filename,
entry.attrs.file_type(),
entry.attrs.size.unwrap_or(0));
}
Ok(())
}
async fn create_directory(client: &mut SftpClient, path: &str) -> Result<(), Box<dyn std::error::Error>> {
client.mkdir(path, 0o755).await?;
println!("目录创建成功: {}", path);
Ok(())
}
async fn file_operations(client: &mut SftpClient) -> Result<(), Box<dyn std::error::Error>> {
// 创建测试文件
let test_file = client
.open(
"test_file.txt",
FileOpenOptions::new()
.write(true)
.create(true),
)
.await?;
test_file.write_all(b"Hello, SFTP!").await?;
// 重命名文件
client.rename("test_file.txt", "renamed_file.txt").await?;
// 获取文件属性
let attrs = client.stat("renamed_file.txt").await?;
println!("重命名文件大小: {} bytes", attrs.size.unwrap_or(0));
// 删除文件
client.remove("renamed_file.txt").await?;
Ok(())
}
async fn handle_errors(client: &mut SftpClient) {
match client.stat("nonexistent_file.txt").await {
Ok(attrs) => println!("文件存在,大小: {}", attrs.size.unwrap_or(0)),
Err(russh_sftp::error::Error::Sftp(russh_sftp::protocol::Status::NoSuchFile)) => {
println!("文件不存在")
}
Err(e) => eprintln!("发生错误: {}", e),
}
}
性能优化建议
- 使用缓冲区:对于大文件传输,使用适当的缓冲区大小
- 并发传输:利用异步特性进行多个文件的并发传输
- 连接复用:保持连接打开以进行多个操作
- 错误重试:实现适当的重试机制处理网络波动
注意事项
- 确保服务器支持 SFTP 协议
- 正确处理连接超时和重连
- 注意文件权限和所有权问题
- 在生产环境中使用适当的日志记录和监控
这个库提供了强大而灵活的 SFTP 操作功能,结合 Rust 的安全性和性能优势,是构建可靠文件传输应用的优秀选择。