0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

文盤Rust -- rust連接oss

jf_wN0SrCdH ? 來源:Rust語言中文社區(qū) ? 2023-05-12 16:18 ? 次閱讀

對象存儲是云的基礎(chǔ)組件之一,各大云廠商都有相關(guān)產(chǎn)品。這里跟大家介紹一下rust與對象存儲交到的基本套路和其中的一些技巧。

基本連接

我們以 [S3 sdk](https://github.com/awslabs/aws-sdk-rust)為例來說說基本的連接與操作,作者驗(yàn)證過aws、京東云、阿里云。主要的增刪改查功能沒有什么差別。

基本依賴 Cargo.toml

  # oss
  aws-config = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "main" }
  aws-sdk-s3 = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "main" }
  aws-types = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "main",  feature = ["hardcoded-credentials"] }
  aws-credential-types = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "main" }
  aws-smithy-types = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "main" }

建立客戶端

let shared_config = SdkConfig::builder()
         .credentials_provider(SharedCredentialsProvider::new(
            "LTAI5t7NPuPKsXm6UeSa1",
            "DGHuK03ESXQYqQ83buKMHs9NAwz",
             None,
             None,
             "Static",
         )))
         .endpoint_url("http://oss-cn-beijing.aliyuncs.com")
         .region(Region::new("oss-cn-beijing"))
         .build();
     let s3_config_builder = aws_sdk_s3::from(&shared_config);
     let client = aws_sdk_s3::from_conf(s3_config_builder.build());

建立Client所需要的參數(shù)主要有你需要訪問的oss的AK、SK,endpoint url 以及服務(wù)所在的區(qū)域。以上信息都可以在服務(wù)商的幫助文檔查詢到。

對象列表

let mut obj_list = client
     .list_objects_v2()
     .bucket(bucket)
     .max_keys(max_keys)
     .prefix(prefix_str)
     .continuation_token(token_str);


let list = obj_list.send().await.unwrap();
println!("{:?}",list.contents());
println!("{:?}",list.next_continuation_token());

使用list_objects_v2函數(shù)返回對象列表,相比list_objects函數(shù),list_objects_v2可以通過continuation_token和max_keys控制返回列表的長度。list.contents()返回對象列表數(shù)組,list.next_continuation_token()返回繼續(xù)查詢的token。

上傳文件

let content = ByteStream::from("content in file".as_bytes());
 let exp = aws_smithy_types::from_secs(100);
let upload = client
    .put_object()
    .bucket("bucket")
    .key("/test/key")
    .expires(exp)
    .body(content);
upload.send().await.unwrap();

指定bucket及對象路徑,body接受ByteStream類型作為文件內(nèi)容,最后設(shè)置過期時(shí)間expires,無過期時(shí)間時(shí)不指定該配置即可。

下載文件


	
let key = "/tmp/test/key".to_string();
let resp = client
    .get_object()
    .bucket("bucket")
    .key(&key)
    .send()
    .await.unwrap();
let data = resp.body.collect().await.unwrap();
let bytes = data.into_bytes();


let path = std::new("/tmp/key")
if let Some(p) = path.parent() {
    std::create_dir_all(p).unwrap();
}
let mut file = OpenOptions::new()
    .write(true)
    .truncate(true)
    .create(true)
    .open(path).unwrap();
let _ = file.write(&*bytes);
file.flush().unwrap();

通過get_object()函數(shù)獲取GetObjectOutput。返回值的body 就是文件內(nèi)容,將 body 轉(zhuǎn)換為 bytes,最后打開文件寫入即可。

刪除文件


	
let mut keys = vec![];
let key1 = ObjectIdentifier::builder()
    .set_key(Some("/tmp/key1".to_string()))
    .build();
let key2 = ObjectIdentifier::builder()
    .set_key(Some("/tmp/key2".to_string()))
    .build()
keys.push(key1);
keys.push(key2)
client
    .delete_objects()
    .bucket(bucket)
    .delete(Delete::builder().set_objects(Some(keys)).build())
    .send()
    .await
    .unwrap();

delete_objects 批量刪除對象。首先構(gòu)建keys vector,定義要?jiǎng)h除的對象,然后通過Delete::builder(),構(gòu)建 Delete model。

大文件上傳


	
let mut file = fs::open("/tmp/file_name").unwrap();
let chunk_size = 1024*1024;
let mut part_number = 0;
let mut upload_parts: Vec = Vec::new();


//獲取上傳id
let multipart_upload_res: CreateMultipartUploadOutput = self
    .client
    .create_multipart_upload()
    .bucket("bucket")
    .key("/tmp/key")
    .send()
    .await.unwrap();
let upload_id = match multipart_upload_res.upload_id() {
    Some(id) => id,
    None => {
        return Err(anyhow!("upload id is None"));
    }
};


//分段上傳文件并記錄completer_part
loop {
    let mut buf = vec![0; chuck_size];
    let read_count = file.read(&mut buf)?;
    part_number += 1;


    if read_count == 0 {
        break;
    }


    let body = &buf[..read_count];
    let stream = ByteStream::from(body.to_vec());


    let upload_part_res = self
        .client
        .upload_part()
        .key(key)
        .bucket(bucket)
        .upload_id(upload_id)
        .body(stream)
        .part_number(part_number)
        .send()
        .await.unwrap();


    let completer_part = CompletedPart::builder()
        .e_tag(upload_part_res.e_tag.unwrap_or_default())
        .part_number(part_number)
        .build();


    upload_parts.push(completer_part);


    if read_count != chuck_size {
        break;
    }
}
// 完成上傳文件合并
let completed_multipart_upload: CompletedMultipartUpload =
    CompletedMultipartUpload::builder()
        .set_parts(Some(upload_parts))
        .build();


let _complete_multipart_upload_res = self
    .client
    .complete_multipart_upload()
    .bucket("bucket")
    .key(key)
    .multipart_upload(completed_multipart_upload)
    .upload_id(upload_id)
    .send()
    .await.unwrap();

有時(shí)候面對大文件,比如幾百兆甚至幾個(gè)G的文件,為了節(jié)約帶寬和內(nèi)存,我才采取分段上傳的方案,然后在對象存儲的服務(wù)端做合并?;玖鞒淌牵褐付╞ucket和key,獲取一個(gè)上傳id;按流讀取文件,分段上傳字節(jié)流,并記錄CompletedPart;通知服務(wù)器按照CompletedPart 集合來合并文件。具體過程代碼已加注釋,這里不再累述。

大文件下載


	
let mut file = match OpenOptions::new()
            .truncate(true)
            .create(true)
            .write(true)
            .open("/tmp/target_file");
let key = "/tmp/test/key".to_string();
let resp = client
    .get_object()
    .bucket("bucket")
    .key(&key)
    .send()
    .await.unwrap();


let content_len = resp.content_length();
let mut byte_stream_async_reader = resp.body.into_async_read();
let mut content_len_usize: usize = content_len.try_into().unwrap();
loop {
    if content_len_usize > chunk_size {
        let mut buffer = vec![0; chunk_size];
        let _ = byte_stream_async_reader.read_exact(&mut buffer).await.unwrap();
        file.write_all(&buffer).unwrap();
        content_len_usize -= chunk_size;
        continue;
    } else {
        let mut buffer = vec![0; content_len_usize];
        let _ = byte_stream_async_reader.read_exact(&mut buffer).await.unwrap();
        file.write_all(&buffer).unwrap();
        break;
    }
}
file.flush().unwrap();

在從對象存儲服務(wù)端下載文件的過程中也會遇到大文件問題。為了節(jié)約帶寬和內(nèi)存,我們采取讀取字節(jié)流的方式分段寫入文件。首先get_object()函數(shù)獲取ByteStream,通過async_reader流式讀取對象字節(jié),分段寫入文件。

審核編輯 :李倩


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 對象存儲
    +關(guān)注

    關(guān)注

    0

    文章

    65

    瀏覽量

    11943
  • 存儲服務(wù)
    +關(guān)注

    關(guān)注

    0

    文章

    20

    瀏覽量

    5922
  • Rust
    +關(guān)注

    關(guān)注

    1

    文章

    226

    瀏覽量

    6497

原文標(biāo)題:文盤Rust -- rust連接oss

文章出處:【微信號:Rust語言中文社區(qū),微信公眾號:Rust語言中文社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    如何使用Rust連接Redis

    Redis是一款快速、開源、鍵值存儲數(shù)據(jù)庫,被廣泛應(yīng)用于緩存、發(fā)布/訂閱系統(tǒng)、定時(shí)任務(wù)等場景中。Rust提供了很多Redis的客戶端庫,本教程將會介紹如何使用Rust連接Redis,以及如何通過
    的頭像 發(fā)表于 09-19 16:22 ?1973次閱讀

    如何在Rust中使用Memcached

    Memcached協(xié)議的實(shí)現(xiàn),使得開發(fā)者可以在Rust中使用Memcached。 基礎(chǔ)用法 創(chuàng)建連接 使用Rust語言Memcached需要先創(chuàng)建一個(gè)連接。可以使用 memcached
    的頭像 發(fā)表于 09-19 16:30 ?1094次閱讀

    Rust GUI實(shí)踐之Rust-Qt模塊

    Rust-Qt 是 Rust 語言的一個(gè) Qt 綁定庫,它允許 Rust 開發(fā)者使用 Qt 框架來創(chuàng)建跨平臺的圖形界面應(yīng)用程序。Qt 是一個(gè)跨平臺的應(yīng)用程序框架,它提供了一系列的工具和庫,可以幫助
    的頭像 發(fā)表于 09-30 16:43 ?1376次閱讀

    Rust語言如何與 InfluxDB 集成

    InfluxDB Rust 客戶端??梢栽?Cargo.toml 文件中添加以下依賴項(xiàng): [dependencies] influxdb = "0.14.0" 連接到 InfluxDB 我們需要
    的頭像 發(fā)表于 09-30 16:45 ?955次閱讀

    如何在Rust連接和使用MySQL數(shù)據(jù)庫

    如何在Rust連接和使用MySQL數(shù)據(jù)庫。 安裝 mysql 模塊 這里我們假設(shè)你已經(jīng)安裝了Rust編程語言工具鏈,在本教程中,我們將使用 mysql crate來連接和使用MySQ
    的頭像 發(fā)表于 09-30 17:05 ?1404次閱讀

    只會用Python?教你在樹莓派上開始使用Rust

    結(jié)合起來,并在Raspberry Pi上安裝Rust。設(shè)置Raspberry Pi對于此項(xiàng)目,您將需要:Raspberry PiLED。220-1k歐姆電阻。面包板和接線。使用GPIO 18連接到LED
    發(fā)表于 05-20 08:00

    怎樣去使用Rust進(jìn)行嵌入式編程呢

    使用Rust進(jìn)行嵌入式編程Use Rust for embedded development篇首語:Rust的高性能、可靠性和生產(chǎn)力使其適合于嵌入式系統(tǒng)。在過去的幾年里,Rust在程序
    發(fā)表于 12-22 07:20

    RUST在嵌入式開發(fā)中的應(yīng)用是什么

    Rust是一種編程語言,它使用戶能夠構(gòu)建可靠、高效的軟件,尤其是用于嵌入式開發(fā)的軟件。它的特點(diǎn)是:高性能:Rust具有驚人的速度和高內(nèi)存利用率??煽啃裕涸诰幾g過程中可以消除內(nèi)存錯(cuò)誤。生產(chǎn)效率:優(yōu)秀
    發(fā)表于 12-24 08:34

    Rust代碼中加載靜態(tài)庫時(shí),出現(xiàn)錯(cuò)誤 ` rust-lld: error: undefined symbol: malloc `怎么解決?

    “ [i]malloc ”、“ [i]exit ”。我驗(yàn)證了使用 ` [i]nm ` 命令。 問題是我打算使用 ffi 在 rust 中使用這個(gè)靜態(tài)庫。當(dāng)我嘗試在我的 Rust 代碼中加載靜態(tài)庫
    發(fā)表于 06-09 08:44

    rust-analyzer Rust編譯器前端實(shí)現(xiàn)

    ./oschina_soft/rust-analyzer.zip
    發(fā)表于 05-19 09:23 ?2次下載
    <b class='flag-5'>rust</b>-analyzer <b class='flag-5'>Rust</b>編譯器前端實(shí)現(xiàn)

    rust-av基于rust的多媒體工具包

    ./oschina_soft/rust-av.zip
    發(fā)表于 06-01 11:39 ?1次下載
    <b class='flag-5'>rust</b>-av基于<b class='flag-5'>rust</b>的多媒體工具包

    Chromium正式開始支持Rust

    ? Chromium 正式開始支持 Rust 目前的支持只是第一階段,在C++代碼中使用Rust寫的第三方庫(編譯成.so)。估計(jì)明年Chromium的二進(jìn)制發(fā)行文件中會包含rust寫的庫。更廣
    的頭像 發(fā)表于 01-14 10:04 ?855次閱讀

    Rust -- tokio綁定cpu實(shí)踐

    )。core_affinity_rs是一個(gè)用于管理CPU親和力的Rust crate。目前支持Linux、Mac OSX和Windows。官方宣稱支持多平臺,本人只做了linux 操作系統(tǒng)的測試。
    的頭像 發(fā)表于 06-11 15:32 ?479次閱讀
    <b class='flag-5'>文</b><b class='flag-5'>盤</b><b class='flag-5'>Rust</b> -- tokio綁定cpu實(shí)踐

    Rust的內(nèi)部工作原理

    Rust到匯編:了解 Rust 的內(nèi)部工作原理 非常好的Rust系列文章,通過生成的匯編代碼,讓你了解很多Rust內(nèi)部的工作機(jī)制。例如文章有 Rus
    的頭像 發(fā)表于 06-14 10:34 ?665次閱讀
    <b class='flag-5'>Rust</b>的內(nèi)部工作原理

    從Rustup出發(fā)看Rust編譯生態(tài)

    從Rustup出發(fā)看Rust編譯生態(tài) 1. Rust和LLVM的關(guān)系是怎樣的? 2. Rustup中targets是什么,為什么可以安裝多個(gè)? 3. Rust在windows上為什么需要安裝Visual studio?
    的頭像 發(fā)表于 01-02 11:00 ?397次閱讀