@@ -41,6 +41,7 @@ use object_store::{
4141 BackoffConfig , ClientOptions , ObjectMeta , ObjectStore , PutPayload , RetryConfig ,
4242} ;
4343use relative_path:: { RelativePath , RelativePathBuf } ;
44+ use tokio:: { fs:: OpenOptions , io:: AsyncReadExt } ;
4445use tracing:: { error, info} ;
4546use url:: Url ;
4647
@@ -53,8 +54,8 @@ use crate::{
5354use super :: {
5455 metrics_layer:: MetricLayer , object_storage:: parseable_json_path, to_object_store_path,
5556 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 ,
5859} ;
5960
6061#[ derive( Debug , Clone , clap:: Args ) ]
@@ -378,6 +379,65 @@ impl BlobStore {
378379 res
379380 }
380381
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+
381441 // TODO: introduce parallel, multipart-uploads if required
382442 // async fn _upload_multipart(&self, key: &str, path: &Path) -> Result<(), ObjectStorageError> {
383443 // let mut buf = vec![0u8; MULTIPART_UPLOAD_SIZE / 2];
@@ -426,10 +486,10 @@ impl BlobStore {
426486impl ObjectStorage for BlobStore {
427487 async fn upload_multipart (
428488 & self ,
429- _key : & RelativePath ,
430- _path : & Path ,
489+ key : & RelativePath ,
490+ path : & Path ,
431491 ) -> Result < ( ) , ObjectStorageError > {
432- unimplemented ! ( )
492+ self . _upload_multipart ( key , path ) . await
433493 }
434494 async fn get_buffered_reader (
435495 & self ,
0 commit comments