mirror of
https://github.com/minio/minio-rs.git
synced 2025-12-06 23:36:52 +08:00
Add metadata and other options for CreateMultipart (#90)
- Add validation for user-metadata keys. - Ensure that options are passed to both single part PutObject and Multipart upload when using the PutObjectContent higher level API.
This commit is contained in:
parent
18c5707a4a
commit
af8193aa95
@ -46,6 +46,12 @@ pub struct CreateMultipartUpload {
|
|||||||
region: Option<String>,
|
region: Option<String>,
|
||||||
bucket: String,
|
bucket: String,
|
||||||
object: String,
|
object: String,
|
||||||
|
|
||||||
|
user_metadata: Option<Multimap>,
|
||||||
|
sse: Option<Arc<dyn Sse>>,
|
||||||
|
tags: Option<HashMap<String, String>>,
|
||||||
|
retention: Option<Retention>,
|
||||||
|
legal_hold: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CreateMultipartUpload {
|
impl CreateMultipartUpload {
|
||||||
@ -76,22 +82,61 @@ impl CreateMultipartUpload {
|
|||||||
self.region = region;
|
self.region = region;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn user_metadata(mut self, user_metadata: Option<Multimap>) -> Self {
|
||||||
|
self.user_metadata = user_metadata;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sse(mut self, sse: Option<Arc<dyn Sse>>) -> Self {
|
||||||
|
self.sse = sse;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tags(mut self, tags: Option<HashMap<String, String>>) -> Self {
|
||||||
|
self.tags = tags;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn retention(mut self, retention: Option<Retention>) -> Self {
|
||||||
|
self.retention = retention;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn legal_hold(mut self, legal_hold: bool) -> Self {
|
||||||
|
self.legal_hold = legal_hold;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_headers(&self) -> Result<Multimap, Error> {
|
||||||
|
object_write_args_headers(
|
||||||
|
self.extra_headers.as_ref(),
|
||||||
|
self.user_metadata.as_ref(),
|
||||||
|
&self.sse,
|
||||||
|
self.tags.as_ref(),
|
||||||
|
self.retention.as_ref(),
|
||||||
|
self.legal_hold,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate(&self) -> Result<(), Error> {
|
||||||
|
check_bucket_name(&self.bucket, true)?;
|
||||||
|
|
||||||
|
if self.object.is_empty() {
|
||||||
|
return Err(Error::InvalidObjectName(String::from(
|
||||||
|
"object name cannot be empty",
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToS3Request for CreateMultipartUpload {
|
impl ToS3Request for CreateMultipartUpload {
|
||||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||||
check_bucket_name(&self.bucket, true)?;
|
self.validate()?;
|
||||||
|
|
||||||
let mut headers = Multimap::new();
|
let headers = self.get_headers()?;
|
||||||
if let Some(v) = &self.extra_headers {
|
|
||||||
merge(&mut headers, v);
|
|
||||||
}
|
|
||||||
if !headers.contains_key("Content-Type") {
|
|
||||||
headers.insert(
|
|
||||||
String::from("Content-Type"),
|
|
||||||
String::from("application/octet-stream"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut query_params = Multimap::new();
|
let mut query_params = Multimap::new();
|
||||||
if let Some(v) = &self.extra_query_params {
|
if let Some(v) = &self.extra_query_params {
|
||||||
@ -326,14 +371,17 @@ pub struct UploadPart {
|
|||||||
bucket: String,
|
bucket: String,
|
||||||
object: String,
|
object: String,
|
||||||
region: Option<String>,
|
region: Option<String>,
|
||||||
user_metadata: Option<Multimap>,
|
|
||||||
sse: Option<Arc<dyn Sse>>,
|
sse: Option<Arc<dyn Sse>>,
|
||||||
tags: Option<HashMap<String, String>>,
|
tags: Option<HashMap<String, String>>,
|
||||||
retention: Option<Retention>,
|
retention: Option<Retention>,
|
||||||
legal_hold: bool,
|
legal_hold: bool,
|
||||||
data: SegmentedBytes,
|
data: SegmentedBytes,
|
||||||
|
|
||||||
// These are optional as the struct is reused for PutObject.
|
// This is used only when this struct is used for PutObject.
|
||||||
|
user_metadata: Option<Multimap>,
|
||||||
|
|
||||||
|
// These are only used for multipart UploadPart but not for PutObject, so
|
||||||
|
// they are optional.
|
||||||
upload_id: Option<String>,
|
upload_id: Option<String>,
|
||||||
part_number: Option<u16>,
|
part_number: Option<u16>,
|
||||||
}
|
}
|
||||||
@ -376,11 +424,6 @@ impl UploadPart {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn user_metadata(mut self, user_metadata: Option<Multimap>) -> Self {
|
|
||||||
self.user_metadata = user_metadata;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sse(mut self, sse: Option<Arc<dyn Sse>>) -> Self {
|
pub fn sse(mut self, sse: Option<Arc<dyn Sse>>) -> Self {
|
||||||
self.sse = sse;
|
self.sse = sse;
|
||||||
self
|
self
|
||||||
@ -401,10 +444,9 @@ impl UploadPart {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_headers(&self) -> Multimap {
|
fn get_headers(&self) -> Result<Multimap, Error> {
|
||||||
object_write_args_headers(
|
object_write_args_headers(
|
||||||
self.extra_headers.as_ref(),
|
self.extra_headers.as_ref(),
|
||||||
None,
|
|
||||||
self.user_metadata.as_ref(),
|
self.user_metadata.as_ref(),
|
||||||
&self.sse,
|
&self.sse,
|
||||||
self.tags.as_ref(),
|
self.tags.as_ref(),
|
||||||
@ -446,7 +488,7 @@ impl ToS3Request for UploadPart {
|
|||||||
fn to_s3request(&self) -> Result<S3Request, Error> {
|
fn to_s3request(&self) -> Result<S3Request, Error> {
|
||||||
self.validate()?;
|
self.validate()?;
|
||||||
|
|
||||||
let headers = self.get_headers();
|
let headers = self.get_headers()?;
|
||||||
|
|
||||||
let mut query_params = Multimap::new();
|
let mut query_params = Multimap::new();
|
||||||
if let Some(v) = &self.extra_query_params {
|
if let Some(v) = &self.extra_query_params {
|
||||||
@ -550,24 +592,34 @@ impl S3Api for PutObject {
|
|||||||
|
|
||||||
fn object_write_args_headers(
|
fn object_write_args_headers(
|
||||||
extra_headers: Option<&Multimap>,
|
extra_headers: Option<&Multimap>,
|
||||||
headers: Option<&Multimap>,
|
|
||||||
user_metadata: Option<&Multimap>,
|
user_metadata: Option<&Multimap>,
|
||||||
sse: &Option<Arc<dyn Sse>>,
|
sse: &Option<Arc<dyn Sse>>,
|
||||||
tags: Option<&HashMap<String, String>>,
|
tags: Option<&HashMap<String, String>>,
|
||||||
retention: Option<&Retention>,
|
retention: Option<&Retention>,
|
||||||
legal_hold: bool,
|
legal_hold: bool,
|
||||||
) -> Multimap {
|
) -> Result<Multimap, Error> {
|
||||||
let mut map = Multimap::new();
|
let mut map = Multimap::new();
|
||||||
|
|
||||||
if let Some(v) = extra_headers {
|
if let Some(v) = extra_headers {
|
||||||
merge(&mut map, v);
|
merge(&mut map, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(v) = headers {
|
|
||||||
merge(&mut map, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(v) = user_metadata {
|
if let Some(v) = user_metadata {
|
||||||
|
// Validate it.
|
||||||
|
for (k, _) in v.iter() {
|
||||||
|
if k.is_empty() {
|
||||||
|
return Err(Error::InvalidUserMetadata(String::from(
|
||||||
|
"user metadata key cannot be empty",
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
if !k.starts_with("x-amz-meta-") {
|
||||||
|
return Err(Error::InvalidUserMetadata(format!(
|
||||||
|
"user metadata key '{}' does not start with 'x-amz-meta-'",
|
||||||
|
k
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
merge(&mut map, v);
|
merge(&mut map, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -606,7 +658,15 @@ fn object_write_args_headers(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
map
|
// Set the Content-Type header if not already set.
|
||||||
|
if !map.contains_key("Content-Type") {
|
||||||
|
map.insert(
|
||||||
|
String::from("Content-Type"),
|
||||||
|
String::from("application/octet-stream"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(map)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PutObjectContent takes a `ObjectContent` stream and uploads it to MinIO/S3.
|
// PutObjectContent takes a `ObjectContent` stream and uploads it to MinIO/S3.
|
||||||
@ -777,11 +837,7 @@ impl PutObjectContent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, we start a multipart upload.
|
// Otherwise, we start a multipart upload.
|
||||||
let create_mpu = CreateMultipartUpload::new(&self.bucket, &self.object)
|
let create_mpu = self.to_create_multipart_upload();
|
||||||
.client(&client)
|
|
||||||
.extra_headers(self.extra_headers.clone())
|
|
||||||
.extra_query_params(self.extra_query_params.clone())
|
|
||||||
.region(self.region.clone());
|
|
||||||
|
|
||||||
let create_mpu_resp = create_mpu.send().await?;
|
let create_mpu_resp = create_mpu.send().await?;
|
||||||
|
|
||||||
@ -925,7 +981,8 @@ impl PutObjectContent {
|
|||||||
bucket: self.bucket.clone(),
|
bucket: self.bucket.clone(),
|
||||||
object: self.object.clone(),
|
object: self.object.clone(),
|
||||||
region: self.region.clone(),
|
region: self.region.clone(),
|
||||||
user_metadata: self.user_metadata.clone(),
|
// User metadata is not sent with UploadPart.
|
||||||
|
user_metadata: None,
|
||||||
sse: self.sse.clone(),
|
sse: self.sse.clone(),
|
||||||
tags: self.tags.clone(),
|
tags: self.tags.clone(),
|
||||||
retention: self.retention.clone(),
|
retention: self.retention.clone(),
|
||||||
@ -952,6 +1009,22 @@ impl PutObjectContent {
|
|||||||
upload_id: upload_id.to_string(),
|
upload_id: upload_id.to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_create_multipart_upload(&self) -> CreateMultipartUpload {
|
||||||
|
CreateMultipartUpload {
|
||||||
|
client: self.client.clone(),
|
||||||
|
extra_headers: self.extra_headers.clone(),
|
||||||
|
extra_query_params: self.extra_query_params.clone(),
|
||||||
|
region: self.region.clone(),
|
||||||
|
bucket: self.bucket.clone(),
|
||||||
|
object: self.object.clone(),
|
||||||
|
user_metadata: self.user_metadata.clone(),
|
||||||
|
sse: self.sse.clone(),
|
||||||
|
tags: self.tags.clone(),
|
||||||
|
retention: self.retention.clone(),
|
||||||
|
legal_hold: self.legal_hold,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const MIN_PART_SIZE: u64 = 5 * 1024 * 1024; // 5 MiB
|
pub const MIN_PART_SIZE: u64 = 5 * 1024 * 1024; // 5 MiB
|
||||||
|
|||||||
@ -76,6 +76,7 @@ pub enum Error {
|
|||||||
InvalidObjectName(String),
|
InvalidObjectName(String),
|
||||||
InvalidUploadId(String),
|
InvalidUploadId(String),
|
||||||
InvalidPartNumber(String),
|
InvalidPartNumber(String),
|
||||||
|
InvalidUserMetadata(String),
|
||||||
EmptyParts(String),
|
EmptyParts(String),
|
||||||
InvalidRetentionMode(String),
|
InvalidRetentionMode(String),
|
||||||
InvalidRetentionConfig(String),
|
InvalidRetentionConfig(String),
|
||||||
@ -136,6 +137,7 @@ impl fmt::Display for Error {
|
|||||||
Error::InvalidObjectName(m) => write!(f, "{}", m),
|
Error::InvalidObjectName(m) => write!(f, "{}", m),
|
||||||
Error::InvalidUploadId(m) => write!(f, "{}", m),
|
Error::InvalidUploadId(m) => write!(f, "{}", m),
|
||||||
Error::InvalidPartNumber(m) => write!(f, "{}", m),
|
Error::InvalidPartNumber(m) => write!(f, "{}", m),
|
||||||
|
Error::InvalidUserMetadata(m) => write!(f, "{}", m),
|
||||||
Error::EmptyParts(m) => write!(f, "{}", m),
|
Error::EmptyParts(m) => write!(f, "{}", m),
|
||||||
Error::InvalidRetentionMode(m) => write!(f, "invalid retention mode {}", m),
|
Error::InvalidRetentionMode(m) => write!(f, "invalid retention mode {}", m),
|
||||||
Error::InvalidRetentionConfig(m) => write!(f, "invalid retention configuration; {}", m),
|
Error::InvalidRetentionConfig(m) => write!(f, "invalid retention configuration; {}", m),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user