Rust S3存储插件库s3s的使用,实现高效安全的Amazon S3对象存储操作

Rust S3存储插件库s3s的使用,实现高效安全的Amazon S3对象存储操作

Apache 2.0 licensed Unsafe Forbidden

S3 Service Adapter

crate version docs
s3s Crates.io Docs
s3s-aws Crates.io Docs
s3s-fs Crates.io Docs

这是一个实验性项目,旨在为构建S3兼容服务提供符合人体工程学的适配器。

s3s以通用的hyper服务形式实现了Amazon S3 REST API。S3兼容服务可以专注于S3 API本身,而不必关心HTTP层。

s3s-aws提供了有用的类型并与aws-sdk-s3集成。

s3s-fs基于文件系统实现了S3 API,作为一个示例实现。它设计用于集成测试,可以用来模拟S3客户端。它还提供了一个用于调试的二进制文件。

工作原理

architecture diagram

上图展示了s3s的工作原理。

s3s在调用用户定义的服务之前将HTTP请求转换为操作输入。

s3s在调用用户定义的服务之后将操作输出或错误转换为HTTP响应。

数据类型、序列化和反序列化是从aws-sdk-rust存储库中的smithy模型生成的。我们应用手动修复来解决smithy服务器代码生成中的一些问题,并使s3s现在可以使用。

安全性

S3Service和本项目中的其他适配器没有安全保护。如果它们直接暴露在互联网上,可能会受到攻击。用户需要实现安全增强功能,如HTTP正文长度限制、速率限制和背压。

完整示例代码

use aws_sdk_s3::Client;
use s3s::dto::*;
use s3s::S3ServiceBuilder;
use std::sync::Arc;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 创建AWS S3客户端
    let config = aws_config::load_from_env().await;
    let s3_client = Client::new(&config);
    
    // 创建S3服务适配器
    let service = {
        let s3 = Arc::new(s3_client);
        S3ServiceBuilder::new()
            .with_get_object(move |input| {
                let s3 = Arc::clone(&s3);
                async move {
                    // 调用AWS SDK获取对象
                    let output = s3.get_object().bucket(input.bucket).key(input.key).send().await?;
                    
                    // 转换为s3s响应
                    Ok(GetObjectOutput {
                        body: Some(output.body.collect().await?.into_bytes().into()),
                        ..Default::default()
                    })
                }
            })
            // 可以添加其他操作处理程序
            .build()
    };
    
    // 将服务绑定到HTTP服务器
    let app = hyper::Server::bind(&"0.0.0.0:8000".parse().unwrap())
        .serve(s3s::HyperService::new(service));
    
    println!("Server running at http://0.0.0.0:8000");
    app.await?;
    
    Ok(())
}

这个示例展示了如何:

  1. 创建一个AWS S3客户端
  2. 构建一个S3服务适配器,处理GetObject操作
  3. 将服务绑定到HTTP服务器

您可以根据需要添加其他操作处理程序,如PutObject、ListObjects等。

扩展完整示例代码

下面是一个更完整的示例,包含PutObject和ListObjects操作处理程序:

use aws_sdk_s3::Client;
use s3s::dto::*;
use s3s::S3ServiceBuilder;
use std::sync::Arc;
use bytes::Bytes;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 创建AWS S3客户端
    let config = aws_config::load_from_env().await;
    let s3_client = Client::new(&config);
    
    // 创建S3服务适配器
    let service = {
        let s3 = Arc::new(s3_client);
        S3ServiceBuilder::new()
            // 处理GetObject操作
            .with_get_object(move |input| {
                let s3 = Arc::clone(&s3);
                async move {
                    let output = s3.get_object()
                        .bucket(input.bucket)
                        .key(input.key)
                        .send()
                        .await?;
                    
                    Ok(GetObjectOutput {
                        body: Some(output.body.collect().await?.into_bytes().into()),
                        ..Default::default()
                    })
                }
            })
            // 处理PutObject操作
            .with_put_object(move |input| {
                let s3 = Arc::clone(&s3);
                async move {
                    let body = input.body.ok_or("Missing body")?;
                    let bytes = Bytes::from(body.into_inner());
                    
                    let _output = s3.put_object()
                        .bucket(input.bucket)
                        .key(input.key)
                        .body(bytes.into())
                        .send()
                        .await?;
                    
                    Ok(PutObjectOutput {
                        ..Default::default()
                    })
                }
            })
            // 处理ListObjects操作
            .with_list_objects(move |input| {
                let s3 = Arc::clone(&s3);
                async move {
                    let output = s3.list_objects_v2()
                        .bucket(input.bucket)
                        .prefix(input.prefix.unwrap_or_default())
                        .send()
                        .await?;
                    
                    Ok(ListObjectsOutput {
                        contents: output.contents.map(|list| {
                            list.into_iter().map(|obj| {
                                Object {
                                    key: Some(obj.key.unwrap_or_default()),
                                    size: Some(obj.size.unwrap_or_default() as i64),
                                    ..Default::default()
                                }
                            }).collect()
                        }),
                        ..Default::default()
                    })
                }
            })
            .build()
    };
    
    // 将服务绑定到HTTP服务器
    let app = hyper::Server::bind(&"0.0.0.0:8000".parse().unwrap())
        .serve(s3s::HyperService::new(service));
    
    println!("Server running at http://0.0.0.0:8000");
    app.await?;
    
    Ok(())
}

这个扩展示例增加了:

  1. PutObject操作处理程序 - 用于上传对象到S3
  2. ListObjects操作处理程序 - 用于列出存储桶中的对象

每个操作处理程序都遵循类似的模式:

  1. 从输入参数中提取必要信息
  2. 调用AWS SDK执行相应操作
  3. 将结果转换为s3s的响应格式

您可以根据实际需求继续添加其他S3操作处理程序,或者为现有处理程序添加更多功能和错误处理。


1 回复

Rust S3存储插件库s3s的使用指南

概述

s3s是一个Rust库,用于高效安全地与Amazon S3对象存储服务进行交互。它提供了简洁的API来执行常见的S3操作,如上传、下载、删除对象等。

安装

在Cargo.toml中添加依赖:

[dependencies]
s3s = "0.1"  # 请使用最新版本
tokio = { version = "1.0", features = ["full"] }

基本使用方法

1. 创建S3客户端

use s3s::S3Client;
use s3s::auth::Credentials;

#[tokio::main]
async fn main() {
    let credentials = Credentials::new(
        "YOUR_ACCESS_KEY_ID",
        "YOUR_SECRET_ACCESS_KEY",
        None,  // 可选的token
        None,  // 可选的过期时间
    );
    
    let client = S3Client::new(
        "us-east-1",  // 区域
        credentials,
    ).await.unwrap();
}

2. 上传文件到S3

use std::fs::File;
use std::io::Read;

async fn upload_file(client: &S3Client, bucket: &str, key: &str, file_path: &str) {
    let mut file = File::open(file_path).unwrap();
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    
    client.put_object(bucket, key, contents)
        .await
        .expect("Failed to upload file");
    
    println!("File uploaded successfully to {}/{}", bucket, key);
}

3. 下载文件从S3

use std::fs::File;
use std::io::Write;

async fn download_file(client: &S3Client, bucket: &str, key: &str, output_path: &str) {
    let data = client.get_object(bucket, key)
        .await
        .expect("Failed to download file");
    
    let mut file = File::create(output_path).unwrap();
    file.write_all(&data).unwrap();
    
    println!("File downloaded successfully from {}/{}", bucket, key);
}

4. 列出桶中的对象

async fn list_objects(client: &S3Client, bucket: &str) {
    let objects = client list_objects(bucket)
        .await
        .expect("Failed to list objects");
    
    println!("Objects in bucket {}:", bucket);
    for obj in objects {
        println!("- {}", obj.key);
    }
}

高级功能

1. 使用多部分上传(适合大文件)

use s3s::multipart::MultiPartUpload;

async fn multipart_upload(client: &S3Client, bucket: &str, key: &str, file_path: &str) {
    let mut upload = MultiPartUpload::new(client, bucket, key)
        .await
        .expect("Failed to initiate multipart upload");
    
    let mut file = File::open(file_path).unwrap();
    let mut buffer = vec![0; 5 * 1024 * 1024]; // 5MB chunks
    
    loop {
        let bytes_read = file.read(&mut buffer).unwrap();
        if bytes_read == 0 {
            break;
        }
        
        upload.upload_part(&buffer[..bytes_read])
            .await
            .expect("Failed to upload part");
    }
    
    upload.complete()
        .await
        .expect("Failed to complete upload");
    
    println!("Multipart upload completed for {}/{}", bucket, key);
}

2. 设置对象ACL(访问控制)

use s3s::acl::ObjectAcl;

async fn set_object_acl(client: &S3Client, bucket: &str, key: &str) {
    client.put_object_acl(
        bucket,
        key,
        ObjectAcl::PublicRead  // 或其他ACL选项
    )
    .await
    .expect("Failed to set object ACL");
    
    println!("ACL set for {}/{}", bucket, key);
}

安全注意事项

  1. 永远不要将访问密钥硬编码在代码中,使用环境变量或安全的密钥管理系统
  2. 为不同的应用使用不同的IAM用户和权限
  3. 考虑使用临时安全凭证而不是长期凭证
// 从环境变量读取凭证的更好做法
use std::env;

fn get_credentials_from_env() -> Credentials {
    let access_key = env::var("AWS_ACCESS_KEY_ID").expect("AWS_ACCESS_KEY_ID not set");
    let secret_key = env::var("AWS_SECRET_ACCESS_KEY").expect("AWS_SECRET_ACCESS_KEY not set");
    
    Credentials::new(&access_key, &secret_key, None, None)
}

错误处理

s3s库提供了详细的错误类型,建议妥善处理:

use s3s::error::S3Error;

async fn safe_operation(client: &S3Client) -> Result<(), S3Error> {
    match client.list_objects("my-bucket").await {
        Ok(objects) => {
            for obj in objects {
                println!("Found object: {}", obj.key);
            }
            Ok(())
        },
        Err(S3Error::NoSuchBucket) => {
            eprintln!("Bucket does not exist");
            Ok(())
        },
        Err(e) => {
            eprintln!("S3 operation failed: {}", e);
            Err(e)
        }
    }
}

完整示例代码

下面是一个完整的示例,展示了如何使用s3s库进行基本的S3操作:

use s3s::{S3Client, auth::Credentials};
use std::fs::File;
use std::io::{Read, Write};
use tokio;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 1. 创建S3客户端(从环境变量获取凭证)
    let credentials = Credentials::new(
        &std::env::var("AWS_ACCESS_KEY_ID")?,
        &std::env::var("AWS_SECRET_ACCESS_KEY")?,
        None,
        None,
    );
    
    let client = S3Client::new("us-east-1", credentials).await?;
    
    // 2. 上传文件
    let bucket = "my-test-bucket";
    let file_key = "test.txt";
    let file_path = "./test.txt";
    
    upload_file(&client, bucket, file_key, file_path).await?;
    
    // 3. 列出桶中对象
    list_objects(&client, bucket).await?;
    
    // 4. 下载文件
    let output_path = "./downloaded_test.txt";
    download_file(&client, bucket, file_key, output_path).await?;
    
    Ok(())
}

async fn upload_file(
    client: &S3Client,
    bucket: &str,
    key: &str,
    file_path: &str
) -> Result<(), Box<dyn std::error::Error>> {
    let mut file = File::open(file_path)?;
    let mut contents = Vec::new();
    file.read_to_end(&mut contents)?;
    
    client.put_object(bucket, key, contents).await?;
    println!("File uploaded successfully to {}/{}", bucket, key);
    Ok(())
}

async fn download_file(
    client: &S3Client,
    bucket: &str,
    key: &str,
    output_path: &str
) -> Result<(), Box<dyn std::error::Error>> {
    let data = client.get_object(bucket, key).await?;
    
    let mut file = File::create(output_path)?;
    file.write_all(&data)?;
    
    println!("File downloaded successfully from {}/{}", bucket, key);
    Ok(())
}

async fn list_objects(
    client: &S3Client,
    bucket: &str
) -> Result<(), Box<dyn std::error::Error>> {
    let objects = client.list_objects(bucket).await?;
    
    println!("Objects in bucket {}:", bucket);
    for obj in objects {
        println!("- {}", obj.key);
    }
    Ok(())
}

这个完整示例展示了如何:

  1. 从环境变量获取AWS凭证创建S3客户端
  2. 上传文件到指定S3桶
  3. 列出桶中的所有对象
  4. 下载之前上传的文件

使用前请确保:

  1. 已设置AWS_ACCESS_KEY_ID和AWS_SECRET_ACCESS_KEY环境变量
  2. 指定的S3桶已存在
  3. 本地有测试文件(test.txt)可用于上传
回到顶部