@@ -33,12 +33,18 @@ internal partial class DownloadCommand : BaseCommand<TransferUtilityDownloadResp
3333 public override async Task < TransferUtilityDownloadResponse > ExecuteAsync ( CancellationToken cancellationToken )
3434 {
3535 ValidateRequest ( ) ;
36+
37+ FireTransferInitiatedEvent ( ) ;
38+
3639 GetObjectRequest getRequest = ConvertToGetObjectRequest ( this . _request ) ;
3740
3841 var maxRetries = _s3Client . Config . MaxErrorRetry ;
3942 var retries = 0 ;
4043 bool shouldRetry = false ;
4144 string mostRecentETag = null ;
45+ GetObjectResponse lastSuccessfulResponse = null ;
46+ long ? totalBytesFromResponse = null ; // Track total bytes once we have response headers
47+
4248 do
4349 {
4450 shouldRetry = false ;
@@ -54,6 +60,9 @@ public override async Task<TransferUtilityDownloadResponse> ExecuteAsync(Cancell
5460 using ( var response = await this . _s3Client . GetObjectAsync ( getRequest , cancellationToken )
5561 . ConfigureAwait ( continueOnCapturedContext : false ) )
5662 {
63+ // Capture total bytes from response headers as soon as we get them
64+ totalBytesFromResponse = response . ContentLength ;
65+
5766 if ( ! string . IsNullOrEmpty ( mostRecentETag ) && ! string . Equals ( mostRecentETag , response . ETag ) )
5867 {
5968 //if the eTag changed, we need to retry from the start of the file
@@ -101,6 +110,9 @@ await response.WriteResponseStreamToFileAsync(this._request.FilePath, false, can
101110 await response . WriteResponseStreamToFileAsync ( this . _request . FilePath , true , cancellationToken )
102111 . ConfigureAwait ( continueOnCapturedContext : false ) ;
103112 }
113+
114+ // Store the successful response for the completion event
115+ lastSuccessfulResponse = response ;
104116 }
105117 }
106118 catch ( Exception exception )
@@ -109,6 +121,9 @@ await response.WriteResponseStreamToFileAsync(this._request.FilePath, true, canc
109121 shouldRetry = HandleExceptionForHttpClient ( exception , retries , maxRetries ) ;
110122 if ( ! shouldRetry )
111123 {
124+ // Pass total bytes if we have them from response headers, otherwise -1 for unknown
125+ FireTransferFailedEvent ( this . _request . FilePath , Interlocked . Read ( ref _totalTransferredBytes ) , totalBytesFromResponse ?? - 1 ) ;
126+
112127 if ( exception is IOException )
113128 {
114129 throw ;
@@ -131,8 +146,14 @@ await response.WriteResponseStreamToFileAsync(this._request.FilePath, true, canc
131146 WaitBeforeRetry ( retries ) ;
132147 } while ( shouldRetry ) ;
133148
134- // TODO map and return response
135- return new TransferUtilityDownloadResponse ( ) ;
149+ // Map the response once after successful download
150+ var mappedResponse = ResponseMapper . MapGetObjectResponse ( lastSuccessfulResponse ) ;
151+
152+ long totalTransferredBytes = Interlocked . Read ( ref _totalTransferredBytes ) ;
153+ long totalBytes = lastSuccessfulResponse . ContentLength ;
154+ FireTransferCompletedEvent ( mappedResponse , this . _request . FilePath , totalTransferredBytes , totalBytes ) ;
155+
156+ return mappedResponse ;
136157 }
137158
138159 private static bool HandleExceptionForHttpClient ( Exception exception , int retries , int maxRetries )
0 commit comments