@@ -46,7 +46,11 @@ impl FromStr for S3Prefix {
46
46
47
47
Ok ( S3Prefix {
48
48
bucket,
49
- prefix : parsed. path ( ) [ 1 ..] . into ( ) ,
49
+ prefix : parsed
50
+ . path ( )
51
+ . get ( 1 ..)
52
+ . map ( PathBuf :: from)
53
+ . unwrap_or_default ( ) ,
50
54
} )
51
55
}
52
56
}
@@ -77,28 +81,105 @@ impl ReportWriter for S3Writer {
77
81
mime : & Mime ,
78
82
encoding_type : EncodingType ,
79
83
) -> Fallible < ( ) > {
80
- let mut request = self
81
- . client
82
- . put_object ( )
83
- . body ( aws_smithy_http:: byte_stream:: ByteStream :: from ( s) )
84
- . acl ( aws_sdk_s3:: model:: ObjectCannedAcl :: PublicRead )
85
- . key ( format ! (
86
- "{}/{}" ,
87
- self . prefix,
88
- path. as_ref( ) . to_str( ) . unwrap( )
89
- ) )
90
- . content_type ( mime. to_string ( ) )
91
- . bucket ( self . bucket . clone ( ) ) ;
92
- match encoding_type {
93
- EncodingType :: Plain => { }
94
- EncodingType :: Gzip => {
95
- request = request. content_encoding ( "gzip" ) ;
84
+ // At least 50 MB, then use a multipart upload...
85
+ if s. len ( ) >= 50 * 1024 * 1024 {
86
+ let mut request = self
87
+ . client
88
+ . create_multipart_upload ( )
89
+ . acl ( aws_sdk_s3:: model:: ObjectCannedAcl :: PublicRead )
90
+ . key ( format ! (
91
+ "{}/{}" ,
92
+ self . prefix,
93
+ path. as_ref( ) . to_str( ) . unwrap( )
94
+ ) )
95
+ . content_type ( mime. to_string ( ) )
96
+ . bucket ( self . bucket . clone ( ) ) ;
97
+ match encoding_type {
98
+ EncodingType :: Plain => { }
99
+ EncodingType :: Gzip => {
100
+ request = request. content_encoding ( "gzip" ) ;
101
+ }
96
102
}
97
- }
98
- match self . runtime . block_on ( request. send ( ) ) {
99
- Ok ( _) => Ok ( ( ) ) ,
100
- Err ( e) => {
101
- failure:: bail!( "Failed to upload to {:?}: {:?}" , path. as_ref( ) , e) ;
103
+ let upload = match self . runtime . block_on ( request. send ( ) ) {
104
+ Ok ( u) => u,
105
+ Err ( e) => {
106
+ failure:: bail!( "Failed to upload to {:?}: {:?}" , path. as_ref( ) , e) ;
107
+ }
108
+ } ;
109
+
110
+ let chunk_size = 20 * 1024 * 1024 ;
111
+ let bytes = bytes_1:: Bytes :: from ( s) ;
112
+ let mut part = 1 ;
113
+ let mut start = 0 ;
114
+ let mut parts = aws_sdk_s3:: model:: CompletedMultipartUpload :: builder ( ) ;
115
+ while start < bytes. len ( ) {
116
+ let chunk = bytes. slice ( start..std:: cmp:: min ( start + chunk_size, bytes. len ( ) ) ) ;
117
+
118
+ let request = self
119
+ . client
120
+ . upload_part ( )
121
+ . part_number ( part)
122
+ . body ( chunk. into ( ) )
123
+ . upload_id ( upload. upload_id ( ) . unwrap ( ) )
124
+ . key ( upload. key ( ) . unwrap ( ) )
125
+ . bucket ( self . bucket . clone ( ) ) ;
126
+ match self . runtime . block_on ( request. send ( ) ) {
127
+ Ok ( p) => {
128
+ parts = parts. parts (
129
+ aws_sdk_s3:: model:: CompletedPart :: builder ( )
130
+ . e_tag ( p. e_tag . clone ( ) . unwrap ( ) )
131
+ . part_number ( part)
132
+ . build ( ) ,
133
+ )
134
+ }
135
+ Err ( e) => {
136
+ failure:: bail!( "Failed to upload to {:?}: {:?}" , path. as_ref( ) , e) ;
137
+ }
138
+ } ;
139
+
140
+ start += chunk_size;
141
+ part += 1 ;
142
+ }
143
+
144
+ let request = self
145
+ . client
146
+ . complete_multipart_upload ( )
147
+ . multipart_upload ( parts. build ( ) )
148
+ . upload_id ( upload. upload_id ( ) . unwrap ( ) )
149
+ . key ( upload. key ( ) . unwrap ( ) )
150
+ . bucket ( self . bucket . clone ( ) ) ;
151
+ match self . runtime . block_on ( request. send ( ) ) {
152
+ Ok ( _) => ( ) ,
153
+ Err ( e) => {
154
+ failure:: bail!( "Failed to upload to {:?}: {:?}" , path. as_ref( ) , e) ;
155
+ }
156
+ } ;
157
+
158
+ Ok ( ( ) )
159
+ } else {
160
+ let mut request = self
161
+ . client
162
+ . put_object ( )
163
+ . body ( aws_smithy_http:: byte_stream:: ByteStream :: from ( s) )
164
+ . acl ( aws_sdk_s3:: model:: ObjectCannedAcl :: PublicRead )
165
+ . key ( format ! (
166
+ "{}/{}" ,
167
+ self . prefix,
168
+ path. as_ref( ) . to_str( ) . unwrap( )
169
+ ) )
170
+ . content_type ( mime. to_string ( ) )
171
+ . bucket ( self . bucket . clone ( ) ) ;
172
+ match encoding_type {
173
+ EncodingType :: Plain => { }
174
+ EncodingType :: Gzip => {
175
+ request = request. content_encoding ( "gzip" ) ;
176
+ }
177
+ }
178
+ match self . runtime . block_on ( request. send ( ) ) {
179
+ Ok ( _) => Ok ( ( ) ) ,
180
+ Err ( e) => {
181
+ failure:: bail!( "Failed to upload to {:?}: {:?}" , path. as_ref( ) , e) ;
182
+ }
102
183
}
103
184
}
104
185
}
0 commit comments