@@ -11,6 +11,7 @@ impl AzureDriver {
1111}
1212
1313use crate :: { debug_log, get_config_content, ReceivedFile } ;
14+ use async_trait:: async_trait;
1415use axum:: http:: { HeaderName , HeaderValue } ;
1516use azure_storage:: StorageCredentials ;
1617use azure_storage_blobs:: container:: operations:: BlobItem ;
@@ -26,6 +27,7 @@ use std::io::Read;
2627use std:: io:: Write ;
2728use std:: sync:: Arc ;
2829use tempfile:: Builder ;
30+ use tokio:: io:: AsyncReadExt ;
2931use toml:: Table ;
3032
3133#[ derive( Deserialize ) ]
@@ -75,7 +77,110 @@ fn calculate_checksum(filename: &String, data: &[u8]) {
7577 debug_log ! ( "File: {} Checksum: {}" , filename, digest. to_hex_lowercase( ) ) ;
7678}
7779
78- /// Write file to Azure blob storage
80+ /// Write file to Azure blob storage using streaming (new version)
81+ async fn write_file_to_blob_streaming (
82+ filename : String ,
83+ data : & mut ( dyn tokio:: io:: AsyncRead + Unpin + Send ) ,
84+ cont_type : String ,
85+ owner_email : Option < String > ,
86+ ) -> ( & ' static str , usize ) {
87+ let azure_cfg = Arc :: new ( get_azure_credentials ( "azure" ) ) ;
88+
89+ let storage_account = azure_cfg. account . as_str ( ) ;
90+ let storage_key = azure_cfg. key . clone ( ) ;
91+ let storage_container = azure_cfg. container . as_str ( ) ;
92+ let storage_blob = filename. as_str ( ) ;
93+ let storage_credential = StorageCredentials :: access_key ( storage_account, storage_key) ;
94+ let blob_client = ClientBuilder :: new ( storage_account, storage_credential)
95+ . blob_client ( storage_container, storage_blob) ;
96+
97+ let mut total_bytes_uploaded: usize = 0 ;
98+ let chunk_size = 10 * 1024 * 1024 ; // 10MB chunks
99+ let mut blocks = BlockList :: default ( ) ;
100+
101+ loop {
102+ let mut buffer = vec ! [ 0u8 ; chunk_size] ;
103+ let bytes_read = data. read ( & mut buffer) . await ;
104+ match bytes_read {
105+ Ok ( bytes_read) => {
106+ if bytes_read == 0 {
107+ break ;
108+ }
109+ buffer. truncate ( bytes_read) ;
110+ let block_id = BlockId :: new ( hex:: encode ( total_bytes_uploaded. to_le_bytes ( ) ) ) ;
111+ blocks
112+ . blocks
113+ . push ( BlobBlockType :: Uncommitted ( block_id. clone ( ) ) ) ;
114+ match blob_client. put_block ( block_id, buffer) . await {
115+ Ok ( _) => {
116+ total_bytes_uploaded += bytes_read;
117+ debug_log ! ( "Uploaded {} bytes" , total_bytes_uploaded) ;
118+ }
119+ Err ( e) => {
120+ eprintln ! ( "Error uploading block: {:?}" , e) ;
121+ break ;
122+ }
123+ }
124+ }
125+ Err ( e) => {
126+ eprintln ! ( "Error reading stream: {:?}" , e) ;
127+ break ;
128+ }
129+ }
130+ }
131+ match blob_client
132+ . put_block_list ( blocks)
133+ . content_type ( cont_type)
134+ . await
135+ {
136+ Ok ( _) => {
137+ debug_log ! ( "Block list uploaded" ) ;
138+ let blob_url_res = blob_client. url ( ) ;
139+ match blob_url_res {
140+ Ok ( blob_url) => {
141+ debug_log ! ( "Blob URL: {}" , blob_url) ;
142+ }
143+ Err ( e) => {
144+ eprintln ! ( "Error getting blob URL: {:?}" , e) ;
145+ }
146+ }
147+
148+ // Set owner tag if email is provided
149+ if let Some ( email) = owner_email {
150+ let mut tags = Tags :: new ( ) ;
151+ let sanitized = sanitize_tag_component ( & email) ;
152+ if sanitized != email {
153+ debug_log ! (
154+ "Sanitized owner tag value from '{}' to '{}'" ,
155+ email,
156+ sanitized
157+ ) ;
158+ }
159+ // Ensure non-empty value
160+ let final_value = if sanitized. is_empty ( ) {
161+ "_" . to_string ( )
162+ } else {
163+ sanitized
164+ } ;
165+ tags. insert ( "owner" . to_string ( ) , final_value) ;
166+ match blob_client. set_tags ( tags) . await {
167+ Ok ( _) => {
168+ debug_log ! ( "Owner tag set successfully" ) ;
169+ }
170+ Err ( e) => {
171+ eprintln ! ( "Error setting owner tag: {:?}" , e) ;
172+ }
173+ }
174+ }
175+ }
176+ Err ( e) => {
177+ eprintln ! ( "Error uploading block list: {:?}" , e) ;
178+ }
179+ }
180+ ( "OK" , total_bytes_uploaded)
181+ }
182+
183+ /// Write file to Azure blob storage (legacy version using Vec<u8>)
79184/// TBD: Rework, do not keep whole file as Vec<u8> in memory!!!
80185async fn write_file_to_blob (
81186 filename : String ,
@@ -429,6 +534,7 @@ async fn azure_list_files(directory: String) -> Vec<String> {
429534}
430535
431536/// Implement Driver trait for AzureDriver
537+ #[ async_trait]
432538impl super :: Driver for AzureDriver {
433539 fn write_file (
434540 & self ,
@@ -445,6 +551,16 @@ impl super::Driver for AzureDriver {
445551 } ) ;
446552 filenameret
447553 }
554+ async fn write_file_streaming (
555+ & self ,
556+ filename : String ,
557+ data : & mut ( dyn tokio:: io:: AsyncRead + Unpin + Send ) ,
558+ cont_type : String ,
559+ owner_email : Option < String > ,
560+ ) -> ( String , usize ) {
561+ let ( _status, size) = write_file_to_blob_streaming ( filename. clone ( ) , data, cont_type, owner_email) . await ;
562+ ( filename, size)
563+ }
448564 fn tag_file (
449565 & self ,
450566 filename : String ,
0 commit comments