@@ -41,6 +41,7 @@ use object_store::{
41
41
BackoffConfig , ClientOptions , ObjectMeta , ObjectStore , PutPayload , RetryConfig ,
42
42
} ;
43
43
use relative_path:: { RelativePath , RelativePathBuf } ;
44
+ use tokio:: { fs:: OpenOptions , io:: AsyncReadExt } ;
44
45
use tracing:: { error, info} ;
45
46
use url:: Url ;
46
47
@@ -53,8 +54,8 @@ use crate::{
53
54
use super :: {
54
55
metrics_layer:: MetricLayer , object_storage:: parseable_json_path, to_object_store_path,
55
56
ObjectStorage , ObjectStorageError , ObjectStorageProvider , CONNECT_TIMEOUT_SECS ,
56
- PARSEABLE_ROOT_DIRECTORY , REQUEST_TIMEOUT_SECS , SCHEMA_FILE_NAME , STREAM_METADATA_FILE_NAME ,
57
- STREAM_ROOT_DIRECTORY ,
57
+ MIN_MULTIPART_UPLOAD_SIZE , PARSEABLE_ROOT_DIRECTORY , REQUEST_TIMEOUT_SECS , SCHEMA_FILE_NAME ,
58
+ STREAM_METADATA_FILE_NAME , STREAM_ROOT_DIRECTORY ,
58
59
} ;
59
60
60
61
#[ derive( Debug , Clone , clap:: Args ) ]
@@ -378,6 +379,65 @@ impl BlobStore {
378
379
res
379
380
}
380
381
382
+ async fn _upload_multipart (
383
+ & self ,
384
+ key : & RelativePath ,
385
+ path : & Path ,
386
+ ) -> Result < ( ) , ObjectStorageError > {
387
+ let mut file = OpenOptions :: new ( ) . read ( true ) . open ( path) . await ?;
388
+ let location = & to_object_store_path ( key) ;
389
+
390
+ let mut async_writer = self . client . put_multipart ( location) . await ?;
391
+
392
+ let meta = file. metadata ( ) . await ?;
393
+ let total_size = meta. len ( ) as usize ;
394
+ if total_size < MIN_MULTIPART_UPLOAD_SIZE {
395
+ let mut data = Vec :: new ( ) ;
396
+ file. read_to_end ( & mut data) . await ?;
397
+ self . client . put ( location, data. into ( ) ) . await ?;
398
+ // async_writer.put_part(data.into()).await?;
399
+ // async_writer.complete().await?;
400
+ return Ok ( ( ) ) ;
401
+ } else {
402
+ let mut data = Vec :: new ( ) ;
403
+ file. read_to_end ( & mut data) . await ?;
404
+
405
+ // let mut upload_parts = Vec::new();
406
+
407
+ let has_final_partial_part = total_size % MIN_MULTIPART_UPLOAD_SIZE > 0 ;
408
+ let num_full_parts = total_size / MIN_MULTIPART_UPLOAD_SIZE ;
409
+ let total_parts = num_full_parts + if has_final_partial_part { 1 } else { 0 } ;
410
+
411
+ // Upload each part
412
+ for part_number in 0 ..( total_parts) {
413
+ let start_pos = part_number * MIN_MULTIPART_UPLOAD_SIZE ;
414
+ let end_pos = if part_number == num_full_parts && has_final_partial_part {
415
+ // Last part might be smaller than 5MB (which is allowed)
416
+ total_size
417
+ } else {
418
+ // All other parts must be at least 5MB
419
+ start_pos + MIN_MULTIPART_UPLOAD_SIZE
420
+ } ;
421
+
422
+ // Extract this part's data
423
+ let part_data = data[ start_pos..end_pos] . to_vec ( ) ;
424
+
425
+ // Upload the part
426
+ async_writer. put_part ( part_data. into ( ) ) . await ?;
427
+
428
+ // upload_parts.push(part_number as u64 + 1);
429
+ }
430
+ match async_writer. complete ( ) . await {
431
+ Ok ( _) => { }
432
+ Err ( err) => {
433
+ error ! ( "Failed to complete multipart upload. {:?}" , err) ;
434
+ async_writer. abort ( ) . await ?;
435
+ }
436
+ } ;
437
+ }
438
+ Ok ( ( ) )
439
+ }
440
+
381
441
// TODO: introduce parallel, multipart-uploads if required
382
442
// async fn _upload_multipart(&self, key: &str, path: &Path) -> Result<(), ObjectStorageError> {
383
443
// let mut buf = vec![0u8; MULTIPART_UPLOAD_SIZE / 2];
@@ -426,10 +486,10 @@ impl BlobStore {
426
486
impl ObjectStorage for BlobStore {
427
487
async fn upload_multipart (
428
488
& self ,
429
- _key : & RelativePath ,
430
- _path : & Path ,
489
+ key : & RelativePath ,
490
+ path : & Path ,
431
491
) -> Result < ( ) , ObjectStorageError > {
432
- unimplemented ! ( )
492
+ self . _upload_multipart ( key , path ) . await
433
493
}
434
494
async fn get_buffered_reader (
435
495
& self ,
0 commit comments