/*******************************************************************************
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use
* this file except in compliance with the License. A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file.
* This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
* *****************************************************************************
* __ _ _ ___
* ( )( \/\/ )/ __)
* /__\ \ / \__ \
* (_)(_) \/\/ (___/
*
* AWS SDK for .NET
* API Version: 2006-03-01
*
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using Amazon.Runtime.Internal.Util;
using Amazon.S3.Model;
using Amazon.S3.Transfer.Internal;
using Amazon.Util;
using System.Globalization;
namespace Amazon.S3.Transfer
{
///
///
/// Provides a high level utility for managing transfers to and from Amazon S3.
///
///
/// TransferUtility provides a simple API for
/// uploading content to and downloading content
/// from Amazon S3. It makes extensive use of Amazon S3 multipart uploads to
/// achieve enhanced throughput, performance, and reliability.
///
///
/// When uploading large files by specifying file paths instead of a stream,
/// TransferUtility uses multiple threads to upload
/// multiple parts of a single upload at once. When dealing with large content
/// sizes and high bandwidth, this can increase throughput significantly.
///
///
///
///
/// Transfers are stored in memory. If the application is restarted,
/// previous transfers are no longer accessible. In this situation, if necessary,
/// you should clean up any multipart uploads that are incomplete.
///
///
public partial class TransferUtility : ITransferUtility
{
TransferUtilityConfig _config;
IAmazonS3 _s3Client;
bool _shouldDispose = false;
bool _isDisposed;
private HashSet blockedServiceNames = new HashSet
{
"s3-object-lambda"
};
#region Constructors
///
/// Constructs a new class.
///
///
/// The AWS Access Key ID.
///
///
/// The AWS Secret Access Key.
///
///
///
/// If a Timeout needs to be specified, use the constructor which takes an as a paramater.
/// Use an instance of constructed with an object with the Timeout specified.
///
///
public TransferUtility(string awsAccessKeyId, string awsSecretAccessKey)
: this(new AmazonS3Client(awsAccessKeyId, awsSecretAccessKey))
{
this._shouldDispose = true;
}
///
/// Constructs a new class.
///
///
/// The AWS Access Key ID.
///
///
/// The AWS Secret Access Key.
///
///
/// The region to configure the transfer utility for.
///
///
///
/// If a Timeout needs to be specified, use the constructor which takes an as a paramater.
/// Use an instance of constructed with an object with the Timeout specified.
///
///
public TransferUtility(string awsAccessKeyId, string awsSecretAccessKey, RegionEndpoint region)
: this(new AmazonS3Client(awsAccessKeyId, awsSecretAccessKey, region))
{
this._shouldDispose = true;
}
///
/// Constructs a new instance of the class.
///
///
/// The AWS Access Key ID.
///
///
/// The AWS Secret Access Key.
///
///
/// Specifies advanced settings.
///
///
///
/// If a Timeout needs to be specified, use the constructor which takes an as a paramater.
/// Use an instance of constructed with an object with the Timeout specified.
///
///
public TransferUtility(string awsAccessKeyId, string awsSecretAccessKey, TransferUtilityConfig config)
: this(new AmazonS3Client(awsAccessKeyId, awsSecretAccessKey), config)
{
this._shouldDispose = true;
}
///
/// Constructs a new instance of the class.
///
///
/// The AWS Access Key ID.
///
///
/// The AWS Secret Access Key.
///
///
/// The region to configure the transfer utility for.
///
///
/// Specifies advanced settings.
///
///
///
/// If a Timeout needs to be specified, use the constructor which takes an as a paramater.
/// Use an instance of constructed with an object with the Timeout specified.
///
///
public TransferUtility(string awsAccessKeyId, string awsSecretAccessKey, RegionEndpoint region, TransferUtilityConfig config)
: this(new AmazonS3Client(awsAccessKeyId, awsSecretAccessKey, region), config)
{
this._shouldDispose = true;
}
///
/// Constructs a new instance of the class.
///
///
/// The Amazon S3 client.
///
///
///
/// If a Timeout needs to be specified, use the constructor which takes an as a paramater.
/// Use an instance of constructed with an object with the Timeout specified.
///
///
public TransferUtility(IAmazonS3 s3Client)
: this(s3Client, new TransferUtilityConfig())
{
}
///
/// Initializes a new instance of the class.
///
///
/// The Amazon S3 client.
///
///
/// Specifies advanced configuration settings for .
///
///
///
/// If a Timeout needs to be specified, use the constructor which takes an as a paramater.
/// Use an instance of constructed with an object with the Timeout specified.
///
///
public TransferUtility(IAmazonS3 s3Client, TransferUtilityConfig config)
{
this._s3Client = s3Client;
this._config = config;
}
///
/// Constructs a new class.
///
///
///
/// If a Timeout needs to be specified, use the constructor which takes an as a paramater.
/// Use an instance of constructed with an object with the Timeout specified.
///
///
public TransferUtility()
: this(new AmazonS3Client())
{
this._shouldDispose = true;
}
///
/// Constructs a new class.
///
///
/// The region to configure the transfer utility for.
///
///
///
/// If a Timeout needs to be specified, use the constructor which takes an as a paramater.
/// Use an instance of constructed with an object with the Timeout specified.
///
///
public TransferUtility(RegionEndpoint region)
: this(new AmazonS3Client(region))
{
this._shouldDispose = true;
}
///
/// Constructs a new class.
///
///
/// Specifies advanced configuration settings for .
///
///
///
public TransferUtility(TransferUtilityConfig config)
: this(new AmazonS3Client(), config)
{
this._shouldDispose = true;
this._config = config;
}
#endregion
#region Properties
///
/// Gets the Amazon S3 client used for making calls into Amazon S3.
///
///
/// The Amazon S3 client used for making calls into Amazon S3.
///
public IAmazonS3 S3Client
{
get
{
return this._s3Client;
}
}
#endregion
#region Dispose Pattern Implementation
///
/// Implements the Dispose pattern
///
/// Whether this object is being disposed via a call to Dispose
/// or garbage collected.
protected virtual void Dispose(bool disposing)
{
if (!this._isDisposed)
{
if (disposing && _s3Client != null && _shouldDispose)
{
_s3Client.Dispose();
_s3Client = null;
}
this._isDisposed = true;
}
}
///
/// Disposes of all managed and unmanaged resources.
///
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
private void CheckForBlockedArn(string bucketName, string command)
{
if (Arn.IsArn(bucketName))
{
Arn s3Arn = Arn.Parse(bucketName);
if (blockedServiceNames.Contains(s3Arn.Service))
{
if (s3Arn.IsService("s3-object-lambda"))
throw new AmazonS3Exception($"{command} does not support S3 Object Lambda resources");
}
}
}
private static TransferUtilityUploadRequest ConstructUploadRequest(string filePath, string bucketName)
{
if (string.IsNullOrEmpty(filePath))
{
throw new ArgumentNullException("filePath");
}
#if BCL // Validations for Win RT/Win Phone are done in GetUploadCommand method's call to validate.
if (!File.Exists(filePath))
{
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The file {0} does not exist!", filePath));
}
#endif
return new TransferUtilityUploadRequest()
{
BucketName = bucketName,
FilePath = filePath
};
}
private static TransferUtilityUploadRequest ConstructUploadRequest(string filePath, string bucketName, string key)
{
if (string.IsNullOrEmpty(filePath))
{
throw new ArgumentNullException("filePath");
}
#if BCL // Validations for Win RT/Win Phone are done in GetUploadCommand method's call to validate.
if (!File.Exists(filePath))
{
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The file {0} does not exist!", filePath));
}
# endif
return new TransferUtilityUploadRequest()
{
BucketName = bucketName,
Key = key,
FilePath = filePath
};
}
private static TransferUtilityUploadRequest ConstructUploadRequest(Stream stream, string bucketName, string key)
{
if (stream == null)
{
throw new ArgumentNullException("stream");
}
if (string.IsNullOrEmpty(key))
{
throw new ArgumentNullException("key");
}
return new TransferUtilityUploadRequest()
{
BucketName = bucketName,
Key = key,
InputStream = stream
};
}
internal BaseCommand GetUploadCommand(TransferUtilityUploadRequest request)
{
validate(request);
if (IsMultipartUpload(request))
{
return new MultipartUploadCommand(this._s3Client, this._config, request);
}
else
{
return new SimpleUploadCommand(this._s3Client, this._config, request);
}
}
bool IsMultipartUpload(TransferUtilityUploadRequest request)
{
return request.ContentLength >= this._config.MinSizeBeforePartUpload;
}
static void validate(TransferUtilityUploadRequest request)
{
if (request == null)
{
throw new ArgumentNullException("request");
}
if (!request.IsSetBucketName())
{
throw new InvalidOperationException("Please specify BucketName to PUT an object into Amazon S3.");
}
if (!request.IsSetFilePath() &&
!request.IsSetInputStream())
{
throw new InvalidOperationException(
"Please specify either a Filename or provide a Stream to PUT an object into Amazon S3.");
}
if (!request.IsSetKey())
{
if (request.IsSetFilePath())
{
request.Key = Path.GetFileName(request.FilePath);
}
else
{
throw new ArgumentException(
"The Key property must be specified when using a Stream to upload into Amazon S3.");
}
}
if (request.IsSetFilePath())
{
bool fileExists;
fileExists = File.Exists(request.FilePath);
if (!fileExists)
{
throw new ArgumentException("The file indicated by the FilePath property does not exist!");
}
}
}
private static TransferUtilityDownloadRequest ConstructDownloadRequest(string filePath, string bucketName, string key)
{
return new TransferUtilityDownloadRequest()
{
BucketName = bucketName,
Key = key,
FilePath = filePath
};
}
private static TransferUtilityDownloadDirectoryRequest ConstructDownloadDirectoryRequest(string bucketName, string s3Directory, string localDirectory)
{
return new TransferUtilityDownloadDirectoryRequest()
{
BucketName = bucketName,
S3Directory = s3Directory,
LocalDirectory = localDirectory
};
}
static void validate(TransferUtilityUploadDirectoryRequest request)
{
if (!request.IsSetDirectory())
{
throw new InvalidOperationException("Directory not specified");
}
if (!request.IsSetBucketName())
{
throw new InvalidOperationException("BucketName not specified");
}
if (!Directory.Exists(request.Directory))
{
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "The directory {0} does not exists!",
request.Directory));
}
}
private static TransferUtilityUploadDirectoryRequest ConstructUploadDirectoryRequest(string directory, string bucketName)
{
return new TransferUtilityUploadDirectoryRequest()
{
BucketName = bucketName,
Directory = directory
};
}
private static TransferUtilityUploadDirectoryRequest ConstructUploadDirectoryRequest(string directory, string bucketName, string searchPattern, SearchOption searchOption)
{
return new TransferUtilityUploadDirectoryRequest()
{
BucketName = bucketName,
Directory = directory,
SearchPattern = searchPattern,
SearchOption = searchOption
};
}
}
}