@@ -42,6 +42,10 @@ use url::Url;
4242/// Default metadata endpoint
4343static DEFAULT_METADATA_ENDPOINT : & str = "http://169.254.169.254" ;
4444
45+ /// AWS S3 does not support copy operations larger than 5 GiB in a single request. See [Copy
46+ /// Object](https://docs.aws.amazon.com/AmazonS3/latest/userguide/copy-object.html) for reference.
47+ const MAX_SINGLE_REQUEST_COPY_SIZE : u64 = 5 * 1024 * 1024 * 1024 ;
48+
4549/// A specialized `Error` for object store-related errors
4650#[ derive( Debug , thiserror:: Error ) ]
4751enum Error {
@@ -186,6 +190,10 @@ pub struct AmazonS3Builder {
186190 request_payer : ConfigValue < bool > ,
187191 /// The [`HttpConnector`] to use
188192 http_connector : Option < Arc < dyn HttpConnector > > ,
193+ /// Threshold (bytes) above which copy uses multipart copy. If not set, defaults to 5 GiB.
194+ multipart_copy_threshold : Option < ConfigValue < u64 > > ,
195+ /// Preferred multipart copy part size (bytes). If not set, defaults to 5 GiB.
196+ multipart_copy_part_size : Option < ConfigValue < u64 > > ,
189197}
190198
191199/// Configuration keys for [`AmazonS3Builder`]
@@ -396,6 +404,10 @@ pub enum AmazonS3ConfigKey {
396404
397405 /// Encryption options
398406 Encryption ( S3EncryptionConfigKey ) ,
407+ /// Threshold (bytes) to switch to multipart copy
408+ MultipartCopyThreshold ,
409+ /// Preferred multipart copy part size (bytes)
410+ MultipartCopyPartSize ,
399411}
400412
401413impl AsRef < str > for AmazonS3ConfigKey {
@@ -428,6 +440,8 @@ impl AsRef<str> for AmazonS3ConfigKey {
428440 Self :: RequestPayer => "aws_request_payer" ,
429441 Self :: Client ( opt) => opt. as_ref ( ) ,
430442 Self :: Encryption ( opt) => opt. as_ref ( ) ,
443+ Self :: MultipartCopyThreshold => "aws_multipart_copy_threshold" ,
444+ Self :: MultipartCopyPartSize => "aws_multipart_copy_part_size" ,
431445 }
432446 }
433447}
@@ -466,6 +480,12 @@ impl FromStr for AmazonS3ConfigKey {
466480 "aws_conditional_put" | "conditional_put" => Ok ( Self :: ConditionalPut ) ,
467481 "aws_disable_tagging" | "disable_tagging" => Ok ( Self :: DisableTagging ) ,
468482 "aws_request_payer" | "request_payer" => Ok ( Self :: RequestPayer ) ,
483+ "aws_multipart_copy_threshold" | "multipart_copy_threshold" => {
484+ Ok ( Self :: MultipartCopyThreshold )
485+ }
486+ "aws_multipart_copy_part_size" | "multipart_copy_part_size" => {
487+ Ok ( Self :: MultipartCopyPartSize )
488+ }
469489 // Backwards compatibility
470490 "aws_allow_http" => Ok ( Self :: Client ( ClientConfigKey :: AllowHttp ) ) ,
471491 "aws_server_side_encryption" => Ok ( Self :: Encryption (
@@ -631,6 +651,12 @@ impl AmazonS3Builder {
631651 self . encryption_customer_key_base64 = Some ( value. into ( ) )
632652 }
633653 } ,
654+ AmazonS3ConfigKey :: MultipartCopyThreshold => {
655+ self . multipart_copy_threshold = Some ( ConfigValue :: Deferred ( value. into ( ) ) )
656+ }
657+ AmazonS3ConfigKey :: MultipartCopyPartSize => {
658+ self . multipart_copy_part_size = Some ( ConfigValue :: Deferred ( value. into ( ) ) )
659+ }
634660 } ;
635661 self
636662 }
@@ -698,6 +724,14 @@ impl AmazonS3Builder {
698724 self . encryption_customer_key_base64 . clone ( )
699725 }
700726 } ,
727+ AmazonS3ConfigKey :: MultipartCopyThreshold => self
728+ . multipart_copy_threshold
729+ . as_ref ( )
730+ . map ( |x| x. to_string ( ) ) ,
731+ AmazonS3ConfigKey :: MultipartCopyPartSize => self
732+ . multipart_copy_part_size
733+ . as_ref ( )
734+ . map ( |x| x. to_string ( ) ) ,
701735 }
702736 }
703737
@@ -990,6 +1024,18 @@ impl AmazonS3Builder {
9901024 self
9911025 }
9921026
1027+ /// Set threshold (bytes) above which copy uses multipart copy
1028+ pub fn with_multipart_copy_threshold ( mut self , threshold_bytes : u64 ) -> Self {
1029+ self . multipart_copy_threshold = Some ( ConfigValue :: Parsed ( threshold_bytes) ) ;
1030+ self
1031+ }
1032+
1033+ /// Set preferred multipart copy part size (bytes)
1034+ pub fn with_multipart_copy_part_size ( mut self , part_size_bytes : u64 ) -> Self {
1035+ self . multipart_copy_part_size = Some ( ConfigValue :: Parsed ( part_size_bytes) ) ;
1036+ self
1037+ }
1038+
9931039 /// Create a [`AmazonS3`] instance from the provided values,
9941040 /// consuming `self`.
9951041 pub fn build ( mut self ) -> Result < AmazonS3 > {
@@ -1147,6 +1193,17 @@ impl AmazonS3Builder {
11471193 S3EncryptionHeaders :: default ( )
11481194 } ;
11491195
1196+ let multipart_copy_threshold = self
1197+ . multipart_copy_threshold
1198+ . map ( |val| val. get ( ) )
1199+ . transpose ( ) ?
1200+ . unwrap_or ( MAX_SINGLE_REQUEST_COPY_SIZE ) ;
1201+ let multipart_copy_part_size = self
1202+ . multipart_copy_part_size
1203+ . map ( |val| val. get ( ) )
1204+ . transpose ( ) ?
1205+ . unwrap_or ( MAX_SINGLE_REQUEST_COPY_SIZE ) ;
1206+
11501207 let config = S3Config {
11511208 region,
11521209 endpoint : self . endpoint ,
@@ -1164,6 +1221,8 @@ impl AmazonS3Builder {
11641221 conditional_put : self . conditional_put . get ( ) ?,
11651222 encryption_headers,
11661223 request_payer : self . request_payer . get ( ) ?,
1224+ multipart_copy_threshold,
1225+ multipart_copy_part_size,
11671226 } ;
11681227
11691228 let http_client = http. connect ( & config. client_options ) ?;
0 commit comments