public override async Task ExecuteAsync(CancellationToken cancellationToken)
{
ValidateRequest();
GetObjectRequest getRequest = ConvertToGetObjectRequest(this._request);
var maxRetries = ((AmazonS3Client)_s3Client).Config.MaxErrorRetry;
var retries = 0;
bool shouldRetry = false;
string mostRecentETag = null;
do
{
shouldRetry = false;
if (retries != 0)
{
#if PCL
ByteRange bytesRemaining = await ByteRangeRemainingForDownloadAsync(this._request.FilePath).ConfigureAwait(false);
#else
ByteRange bytesRemaining = ByteRangeRemainingForDownload(this._request.FilePath);
#endif
getRequest.ByteRange = bytesRemaining;
}
try
{
using (var response = await this._s3Client.GetObjectAsync(getRequest, cancellationToken)
.ConfigureAwait(continueOnCapturedContext: false))
{
if (!string.IsNullOrEmpty(mostRecentETag) && !string.Equals(mostRecentETag, response.ETag))
{
//if the eTag changed, we need to retry from the start of the file
mostRecentETag = response.ETag;
getRequest.ByteRange = null;
retries = 0;
shouldRetry = true;
WaitBeforeRetry(retries);
continue;
}
mostRecentETag = response.ETag;
if (retries == 0)
{
/*
* Wipe the local file, if it exists, to handle edge case where:
*
* 1. File foo exists
* 2. We start trying to download, but unsuccesfully write any data
* 3. We retry the download, with retires > 0, thus hitting the else statement below
* 4. We will append to file foo, instead of overwriting it
*
* We counter it with the call below because it's the same call that would be hit
* in WriteResponseStreamToFile. If any exceptions are thrown, they will be the same as before
* to avoid any breaking changes to customers who handle that specific exception in a
* particular manor.
*/
#if PCL
var file = await PCLStorage.FileSystem.Current.GetFileFromPathAsync(this._request.FilePath).ConfigureAwait(false);
if (file != null)
await file.DeleteAsync().ConfigureAwait(false);
#endif
#if BCL
if (File.Exists(this._request.FilePath))
{
using (FileStream temp = new FileStream(this._request.FilePath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read, Amazon.S3.Util.S3Constants.DefaultBufferSize))
{
//Do nothing. Simply using the "using" statement to create and dispose of FileStream temp in the same call.
};
}
#endif
response.WriteObjectProgressEvent += OnWriteObjectProgressEvent;
await response.WriteResponseStreamToFileAsync(this._request.FilePath, false, cancellationToken)
.ConfigureAwait(continueOnCapturedContext: false);
}
else
{
response.WriteObjectProgressEvent += OnWriteObjectProgressEvent;
await response.WriteResponseStreamToFileAsync(this._request.FilePath, true, cancellationToken)
.ConfigureAwait(continueOnCapturedContext: false);
}
}
}
catch (Exception exception)
{
retries++;
shouldRetry = HandleExceptionForHttpClient(exception, retries, maxRetries);
if (!shouldRetry)
{
if (exception is IOException)
{
throw;
}
else if (exception.InnerException is IOException)
{
ExceptionDispatchInfo.Capture(exception.InnerException).Throw();
}
else if (exception is AmazonServiceException ||
exception is AmazonClientException)
{
throw;
}
else
{
throw new AmazonServiceException(exception);
}
}
}
WaitBeforeRetry(retries);
} while (shouldRetry);
}