Add: delete_objects api
This commit is contained in:
parent
1d98890964
commit
22943fffe4
|
@ -27,6 +27,7 @@ serde = { version = "1.0.162", features = ["derive"] }
|
|||
serde_json = "1.0.96"
|
||||
async-trait = "0.1.68"
|
||||
lazy_static = "1.4.0"
|
||||
md-5 = "0.10.5"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
dotenvy = "0.15.7"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use reqwest::{StatusCode};
|
||||
use reqwest::StatusCode;
|
||||
use serde::Deserialize;
|
||||
use thiserror::Error;
|
||||
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
use serde::{ Serialize, Deserialize };
|
||||
|
||||
// #[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
// pub struct DeleteRequest {
|
||||
// #[serde(rename = "Delete")]
|
||||
// pub delete: Delete,
|
||||
// }
|
||||
|
||||
pub trait Boolean {
|
||||
fn to_bool(&self) -> bool;
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum ResponseMode {
|
||||
Quiet,
|
||||
Verbose
|
||||
}
|
||||
|
||||
impl Boolean for ResponseMode {
|
||||
fn to_bool(&self) -> bool {
|
||||
match self {
|
||||
ResponseMode::Quiet => true,
|
||||
ResponseMode::Verbose => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Delete {
|
||||
#[serde(rename = "Quiet")]
|
||||
pub quiet: bool,
|
||||
#[serde(rename = "$value")]
|
||||
pub item: Vec<Item>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum Item {
|
||||
Object(Object),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Object {
|
||||
#[serde(rename = "Key")]
|
||||
pub key_name: String,
|
||||
#[serde(rename = "VersionId")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub version_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct DeleteResult {
|
||||
#[serde(rename = "Deleted")]
|
||||
pub deleted: Deleted,
|
||||
#[serde(rename = "Error")]
|
||||
pub error: Error,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Deleted {
|
||||
#[serde(rename = "Key")]
|
||||
pub key_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Error {
|
||||
#[serde(rename = "Key")]
|
||||
pub key_name: String,
|
||||
#[serde(rename = "Code")]
|
||||
pub code: String,
|
||||
#[serde(rename = "Message")]
|
||||
pub message: String,
|
||||
}
|
|
@ -2,6 +2,7 @@ use serde::{Serialize, Deserialize};
|
|||
|
||||
pub mod object;
|
||||
pub mod bucket;
|
||||
pub mod delete_object;
|
||||
|
||||
// #[derive(Serialize, Deserialize,Debug)]
|
||||
// pub struct ErrorResponse {
|
||||
|
|
186
src/object.rs
186
src/object.rs
|
@ -1,18 +1,17 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use md5::{Md5, Digest};
|
||||
use async_trait::async_trait;
|
||||
use reqwest::{
|
||||
header::{HeaderMap, HeaderValue},
|
||||
Method,
|
||||
};
|
||||
use reqwest::{ header::{ HeaderMap, HeaderValue }, Method };
|
||||
use urlencoding::encode;
|
||||
|
||||
use crate::{
|
||||
client::Client,
|
||||
error::{status_to_response, ObsError},
|
||||
error::{ status_to_response, ObsError },
|
||||
model::{
|
||||
bucket::copy_object::CopyObjectResult,
|
||||
object::{NextPosition, ObjectMeta},
|
||||
object::{ NextPosition, ObjectMeta },
|
||||
delete_object::{ Delete, Object, Item, ResponseMode, Boolean },
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -23,7 +22,7 @@ pub trait ObjectTrait {
|
|||
&self,
|
||||
bucket: S,
|
||||
key: S,
|
||||
object: &[u8],
|
||||
object: &[u8]
|
||||
) -> Result<(), ObsError>;
|
||||
|
||||
/// 复制对象
|
||||
|
@ -31,28 +30,34 @@ pub trait ObjectTrait {
|
|||
&self,
|
||||
bucket: S1,
|
||||
src: S2,
|
||||
dest: S3,
|
||||
) -> Result<CopyObjectResult, ObsError>
|
||||
where
|
||||
S1: AsRef<str> + Send,
|
||||
S2: AsRef<str> + Send,
|
||||
S3: AsRef<str> + Send;
|
||||
dest: S3
|
||||
)
|
||||
-> Result<CopyObjectResult, ObsError>
|
||||
where S1: AsRef<str> + Send, S2: AsRef<str> + Send, S3: AsRef<str> + Send;
|
||||
|
||||
/// 删除对象
|
||||
async fn delete_object<S: AsRef<str> + Send>(&self, bucket: S, key: S) -> Result<(), ObsError>;
|
||||
|
||||
/// 批量删除对象
|
||||
async fn delete_objects<S: AsRef<str> + Send, K: IntoIterator<Item = S> + Send>(
|
||||
&self,
|
||||
bucket: S,
|
||||
keys: K,
|
||||
response_mode: ResponseMode,
|
||||
) -> Result<(), ObsError>;
|
||||
|
||||
/// 获取对象内容
|
||||
async fn get_object<S: AsRef<str> + Send>(
|
||||
&self,
|
||||
bucket: S,
|
||||
key: S,
|
||||
key: S
|
||||
) -> Result<bytes::Bytes, ObsError>;
|
||||
|
||||
/// 获取对象元数据
|
||||
async fn get_object_metadata<S: AsRef<str> + Send>(
|
||||
&self,
|
||||
bucket: S,
|
||||
key: S,
|
||||
key: S
|
||||
) -> Result<ObjectMeta, ObsError>;
|
||||
|
||||
/// 追加写对象
|
||||
|
@ -61,7 +66,7 @@ pub trait ObjectTrait {
|
|||
bucket: S,
|
||||
key: S,
|
||||
appended: &[u8],
|
||||
position: u64,
|
||||
position: u64
|
||||
) -> Result<NextPosition, ObsError>;
|
||||
}
|
||||
|
||||
|
@ -72,23 +77,21 @@ impl ObjectTrait for Client {
|
|||
&self,
|
||||
bucket: S,
|
||||
key: S,
|
||||
object: &[u8],
|
||||
object: &[u8]
|
||||
) -> Result<(), ObsError> {
|
||||
let mut with_headers = HeaderMap::new();
|
||||
with_headers.insert(
|
||||
"Content-Length",
|
||||
HeaderValue::from_str(format!("{}", object.len()).as_str()).unwrap(),
|
||||
HeaderValue::from_str(format!("{}", object.len()).as_str()).unwrap()
|
||||
);
|
||||
let resp = self
|
||||
.do_action(
|
||||
Method::PUT,
|
||||
bucket,
|
||||
key,
|
||||
Some(with_headers),
|
||||
None,
|
||||
Some(object.to_owned()),
|
||||
)
|
||||
.await?;
|
||||
let resp = self.do_action(
|
||||
Method::PUT,
|
||||
bucket,
|
||||
key,
|
||||
Some(with_headers),
|
||||
None,
|
||||
Some(object.to_owned())
|
||||
).await?;
|
||||
let _ = resp.text().await?;
|
||||
|
||||
Ok(())
|
||||
|
@ -99,7 +102,7 @@ impl ObjectTrait for Client {
|
|||
bucket: S,
|
||||
key: S,
|
||||
appended: &[u8],
|
||||
position: u64,
|
||||
position: u64
|
||||
) -> Result<NextPosition, ObsError> {
|
||||
let mut params = HashMap::new();
|
||||
params.insert("append".to_string(), "".into());
|
||||
|
@ -107,23 +110,20 @@ impl ObjectTrait for Client {
|
|||
let mut with_headers = HeaderMap::new();
|
||||
with_headers.insert(
|
||||
"Content-Length",
|
||||
HeaderValue::from_str(format!("{}", appended.len()).as_str()).unwrap(),
|
||||
HeaderValue::from_str(format!("{}", appended.len()).as_str()).unwrap()
|
||||
);
|
||||
let resp = self
|
||||
.do_action(
|
||||
Method::POST,
|
||||
bucket,
|
||||
key,
|
||||
Some(with_headers),
|
||||
Some(params),
|
||||
Some(appended.to_owned()),
|
||||
)
|
||||
.await?;
|
||||
let resp = self.do_action(
|
||||
Method::POST,
|
||||
bucket,
|
||||
key,
|
||||
Some(with_headers),
|
||||
Some(params),
|
||||
Some(appended.to_owned())
|
||||
).await?;
|
||||
let status = resp.status();
|
||||
let headers = resp.headers().clone();
|
||||
if status.is_success() {
|
||||
let next_position = if let Some(next) = headers.get("x-obs-next-append-position")
|
||||
{
|
||||
let next_position = if let Some(next) = headers.get("x-obs-next-append-position") {
|
||||
let next = String::from_utf8_lossy(next.as_bytes()).to_string();
|
||||
match next.parse::<u64>() {
|
||||
Ok(u) => Some(u),
|
||||
|
@ -146,33 +146,26 @@ impl ObjectTrait for Client {
|
|||
&self,
|
||||
bucket: S1,
|
||||
src: S2,
|
||||
dest: S3,
|
||||
) -> Result<CopyObjectResult, ObsError>
|
||||
where
|
||||
S1: AsRef<str> + Send,
|
||||
S2: AsRef<str> + Send,
|
||||
S3: AsRef<str> + Send,
|
||||
dest: S3
|
||||
)
|
||||
-> Result<CopyObjectResult, ObsError>
|
||||
where S1: AsRef<str> + Send, S2: AsRef<str> + Send, S3: AsRef<str> + Send
|
||||
{
|
||||
let mut with_headers = HeaderMap::new();
|
||||
let dest = dest.as_ref().trim_start_matches('/');
|
||||
let src = src.as_ref().trim_start_matches('/');
|
||||
let src = encode(src);
|
||||
let copy_source = format!("/{}/{}", bucket.as_ref(), src);
|
||||
with_headers.insert(
|
||||
"x-obs-copy-source",
|
||||
HeaderValue::from_str(©_source).unwrap(),
|
||||
);
|
||||
with_headers.insert("x-obs-copy-source", HeaderValue::from_str(©_source).unwrap());
|
||||
|
||||
let resp = self
|
||||
.do_action(
|
||||
Method::PUT,
|
||||
bucket,
|
||||
dest,
|
||||
Some(with_headers),
|
||||
None,
|
||||
None::<String>,
|
||||
)
|
||||
.await?;
|
||||
let resp = self.do_action(
|
||||
Method::PUT,
|
||||
bucket,
|
||||
dest,
|
||||
Some(with_headers),
|
||||
None,
|
||||
None::<String>
|
||||
).await?;
|
||||
let status = resp.status();
|
||||
let text = resp.text().await?;
|
||||
status_to_response::<CopyObjectResult>(status, text)
|
||||
|
@ -194,9 +187,51 @@ impl ObjectTrait for Client {
|
|||
|
||||
/// 删除对象
|
||||
async fn delete_object<S: AsRef<str> + Send>(&self, bucket: S, key: S) -> Result<(), ObsError> {
|
||||
let _resp = self
|
||||
.do_action(Method::DELETE, bucket, key, None, None, None::<String>)
|
||||
.await?;
|
||||
let _resp = self.do_action(Method::DELETE, bucket, key, None, None, None::<String>).await?;
|
||||
Ok(())
|
||||
}
|
||||
/// 批量删除对象
|
||||
async fn delete_objects<S: AsRef<str> + Send, K: IntoIterator<Item = S> + Send>(
|
||||
&self,
|
||||
bucket: S,
|
||||
keys: K,
|
||||
response_mode: ResponseMode,
|
||||
) -> Result<(), ObsError> {
|
||||
let mut with_headers = HeaderMap::new();
|
||||
let mut params = HashMap::new();
|
||||
params.insert("delete".to_string(), "".to_string());
|
||||
|
||||
let body = Delete {
|
||||
quiet: response_mode.to_bool(),
|
||||
item: keys
|
||||
.into_iter()
|
||||
.map(|key|
|
||||
Item::Object(Object { key_name: key.as_ref().to_owned(), version_id: None })
|
||||
)
|
||||
.collect::<Vec<Item>>(),
|
||||
};
|
||||
let body = serde_xml_rs::to_string(&body)?;
|
||||
let mut hasher = Md5::new();
|
||||
hasher.update(body.as_bytes());
|
||||
let result = hasher.finalize();
|
||||
|
||||
let val = general_purpose::STANDARD.encode(result);
|
||||
|
||||
with_headers.insert("Content-MD5", HeaderValue::from_str(val.as_str()).unwrap());
|
||||
|
||||
with_headers.insert(
|
||||
"Content-Length",
|
||||
HeaderValue::from_str(format!("{}", body.as_bytes().len()).as_str()).unwrap()
|
||||
);
|
||||
let _resp = self.do_action(
|
||||
Method::POST,
|
||||
bucket,
|
||||
"",
|
||||
Some(with_headers),
|
||||
Some(params),
|
||||
Some(body)
|
||||
).await?;
|
||||
// dbg!(_resp.text().await?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -204,13 +239,11 @@ impl ObjectTrait for Client {
|
|||
async fn get_object<S: AsRef<str> + Send>(
|
||||
&self,
|
||||
bucket: S,
|
||||
key: S,
|
||||
key: S
|
||||
) -> Result<bytes::Bytes, ObsError> {
|
||||
let resp = self
|
||||
.do_action(Method::GET, bucket, key, None, None, None::<String>)
|
||||
.await?
|
||||
.bytes()
|
||||
.await?;
|
||||
.do_action(Method::GET, bucket, key, None, None, None::<String>).await?
|
||||
.bytes().await?;
|
||||
|
||||
Ok(resp)
|
||||
}
|
||||
|
@ -219,11 +252,9 @@ impl ObjectTrait for Client {
|
|||
async fn get_object_metadata<S: AsRef<str> + Send>(
|
||||
&self,
|
||||
bucket: S,
|
||||
key: S,
|
||||
key: S
|
||||
) -> Result<ObjectMeta, ObsError> {
|
||||
let resp = self
|
||||
.do_action(Method::HEAD, bucket, key, None, None, None::<String>)
|
||||
.await?;
|
||||
let resp = self.do_action(Method::HEAD, bucket, key, None, None, None::<String>).await?;
|
||||
let headers = resp.headers();
|
||||
let mut data = HashMap::with_capacity(headers.len());
|
||||
for (key, val) in headers {
|
||||
|
@ -232,8 +263,9 @@ impl ObjectTrait for Client {
|
|||
|
||||
let header_str = serde_json::to_string(&data).map_err(|_e| ObsError::ParseOrConvert)?;
|
||||
|
||||
let data: ObjectMeta =
|
||||
serde_json::from_str(&header_str).map_err(|_e| ObsError::ParseOrConvert)?;
|
||||
let data: ObjectMeta = serde_json
|
||||
::from_str(&header_str)
|
||||
.map_err(|_e| ObsError::ParseOrConvert)?;
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
mod common;
|
||||
use huaweicloud_sdk_rust_obs::{error::ObsError, object::ObjectTrait};
|
||||
use huaweicloud_sdk_rust_obs::{error::ObsError, object::ObjectTrait, model::delete_object::ResponseMode};
|
||||
|
||||
use crate::common::*;
|
||||
|
||||
|
@ -70,3 +70,19 @@ async fn test_append_object() -> Result<(), ObsError> {
|
|||
// obs.delete_object(DEFAULT_BUCKET_NAME, key).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_delete_objects() -> Result<(), ObsError> {
|
||||
let objects = vec![
|
||||
"obs-client-test-delete-object.txt",
|
||||
"obs-client-test-delete-object2.txt",
|
||||
];
|
||||
let obs = create_obs_client()?;
|
||||
for obj in &objects {
|
||||
obs.put_object(DEFAULT_BUCKET_NAME, obj, b"test delete text").await?;
|
||||
}
|
||||
|
||||
obs.delete_objects(DEFAULT_BUCKET_NAME, objects, ResponseMode::Verbose).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue