/*******************************************************************************
* 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;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.IO;
using System.Runtime.Serialization;
using System.Security.AccessControl;
using Amazon.Runtime;
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.Util;
using Amazon.Util.Internal;
namespace Amazon.S3.IO
{
///
/// Mimics the System.IO.DirectoryInfo for a virtual directory in S3. It exposes properties and methods for enumerating directories and files as well as
/// methods manipulate directories.
///
///
/// It is important to keep in mind that S3 is not a filesystem. It is possible for S3 object keys to contain
/// characters which are not not legal file path characters, and so some pre-existing objects in a bucket that were created with
/// other software may not be compatible with this class.
///
public sealed class S3DirectoryInfo : IS3FileSystemInfo
{
const int EVENTUAL_CONSISTENCY_SUCCESS_IN_ROW = 10;
const int EVENTUAL_CONSISTENCY_POLLING_PERIOD = 1000;
const long EVENTUAL_CONSISTENCY_MAX_WAIT = 30000;
private IAmazonS3 s3Client;
private string bucket;
private string key;
internal IAmazonS3 S3Client
{
get
{
return s3Client;
}
}
internal string BucketName
{
get
{
return bucket;
}
}
internal string ObjectKey
{
get
{
return key;
}
}
///
/// Initialize a new instance of the S3DirectoryInfo class for the specified S3 bucket.
///
/// S3 client which is used to access the S3 resources.
/// Name of the S3 bucket.
///
public S3DirectoryInfo(IAmazonS3 s3Client, string bucket)
: this(s3Client, bucket, null)
{
}
///
/// Initialize a new instance of the S3DirectoryInfo class for the specified S3 bucket and S3 object key.
///
/// S3 client which is used to access the S3 resources.
/// Name of the S3 bucket.
/// The S3 object key.
///
public S3DirectoryInfo(IAmazonS3 s3Client, string bucket, string key)
{
if (s3Client == null)
{
throw new ArgumentNullException("s3Client");
}
if (String.IsNullOrEmpty(bucket) && !String.IsNullOrEmpty(key))
{
throw new ArgumentException("key cannot be specified if bucket isn't");
}
this.s3Client = s3Client;
this.bucket = bucket ?? String.Empty;
this.key = key ?? String.Empty;
if (!String.IsNullOrEmpty(bucket) && !String.IsNullOrEmpty(key) && !this.key.EndsWith("\\", StringComparison.Ordinal))
{
this.key = string.Format(CultureInfo.InvariantCulture, "{0}{1}", this.key, "\\");
}
if (String.Equals(this.key, "\\", StringComparison.Ordinal))
{
this.key = String.Empty;
}
}
///
/// The S3DirectoryInfo for the root of the S3 bucket.
///
public S3DirectoryInfo Bucket
{
get
{
return new S3DirectoryInfo(s3Client, bucket, "");
}
}
///
/// Checks with S3 to see if the directory exists and if so returns true.
///
/// Due to Amazon S3's eventual consistency model this property can return false for newly created buckets.
///
///
///
public bool Exists
{
get
{
bool bucketExists;
return ExistsWithBucketCheck(out bucketExists);
}
}
internal bool ExistsWithBucketCheck(out bool bucketExists)
{
bucketExists = true;
try
{
if (String.IsNullOrEmpty(bucket))
{
return true;
}
else if (String.IsNullOrEmpty(key))
{
var request = new GetBucketLocationRequest()
{
BucketName = bucket
};
((Amazon.Runtime.Internal.IAmazonWebServiceRequest)request).AddBeforeRequestHandler(S3Helper.FileIORequestEventHandler);
try
{
s3Client.GetBucketLocation(request);
return true;
}
catch(AmazonS3Exception e)
{
if(string.Equals(e.ErrorCode, "NoSuchBucket"))
{
return false;
}
throw;
}
}
else
{
var request = new ListObjectsRequest()
{
BucketName = this.bucket,
Prefix = S3Helper.EncodeKey(key),
MaxKeys = 1
};
((Amazon.Runtime.Internal.IAmazonWebServiceRequest)request).AddBeforeRequestHandler(S3Helper.FileIORequestEventHandler);
var response = s3Client.ListObjects(request);
return response.S3Objects.Count > 0;
}
}
catch (AmazonS3Exception e)
{
if (string.Equals(e.ErrorCode, "NoSuchBucket"))
{
bucketExists = false;
return false;
}
else if (string.Equals(e.ErrorCode, "NotFound"))
{
return false;
}
throw;
}
}
///
/// Returns empty string for directories.
///
string IS3FileSystemInfo.Extension
{
get
{
return String.Empty;
}
}
///
/// The full path of the directory including bucket name.
///
public string FullName
{
get
{
return string.Format(CultureInfo.InvariantCulture, "{0}:\\{1}", bucket, key);
}
}
///
/// Returns the last write time of the the latest file written to the directory.
///
///
///
public DateTime LastWriteTime
{
get
{
DateTime ret = DateTime.MinValue;
if (Exists)
{
if (String.IsNullOrEmpty(this.bucket))
{
ret = DateTime.MinValue;
var listRequest = new ListBucketsRequest();
((Amazon.Runtime.Internal.IAmazonWebServiceRequest)listRequest).AddBeforeRequestHandler(S3Helper.FileIORequestEventHandler);
foreach (S3Bucket s3Bucket in s3Client.ListBuckets(listRequest).Buckets)
{
DateTime currentBucketLastWriteTime = new S3DirectoryInfo(s3Client, s3Bucket.BucketName, String.Empty).LastWriteTime;
if (currentBucketLastWriteTime > ret)
{
ret = currentBucketLastWriteTime;
}
}
}
else
{
var request = new ListObjectsRequest
{
BucketName = bucket,
Prefix = S3Helper.EncodeKey(key)
};
((Amazon.Runtime.Internal.IAmazonWebServiceRequest)request).AddBeforeRequestHandler(S3Helper.FileIORequestEventHandler);
S3Object lastWrittenObject =
((IEnumerable)
PaginatedResourceFactory.Create(new PaginatedResourceInfo()
.WithClient(s3Client)
.WithItemListPropertyPath("S3Objects")
.WithMethodName("ListObjects")
.WithRequest(request)
.WithTokenRequestPropertyPath("Marker")
.WithTokenResponsePropertyPath("NextMarker")))
.OrderByDescending(s3Object => s3Object.LastModified)
.FirstOrDefault();
if (lastWrittenObject != null)
{
ret = lastWrittenObject.LastModified;
}
}
}
return ret;
}
}
///
/// UTC converted version of LastWriteTime.
///
///
///
public DateTime LastWriteTimeUtc
{
get
{
return LastWriteTime.ToUniversalTime();
}
}
///
/// Returns the name of the folder.
///
public string Name
{
get
{
string ret = String.Empty;
if (!String.IsNullOrEmpty(bucket))
{
if (String.IsNullOrEmpty(key))
{
ret = bucket;
}
else
{
int end = key.LastIndexOf('\\');
int start = key.LastIndexOf('\\', end - 1);
return key.Substring(start + 1, end - start - 1);
}
}
return ret;
}
}
///
/// Return the S3DirectoryInfo of the parent directory.
///
public S3DirectoryInfo Parent
{
get
{
S3DirectoryInfo ret = null;
if (!String.IsNullOrEmpty(bucket) && !String.IsNullOrEmpty(key))
{
int last = key.LastIndexOf('\\');
int secondlast = key.LastIndexOf('\\', last - 1);
if (secondlast == -1)
{
ret = Bucket;
}
else
{
var bucketName = key.Substring(0, secondlast);
ret = new S3DirectoryInfo(s3Client, bucket, bucketName);
}
}
if (ret == null)
{
ret = Root;
}
return ret;
}
}
///
/// Returns the S3DirectroyInfo for the S3 account.
///
public S3DirectoryInfo Root
{
get
{
return new S3DirectoryInfo(s3Client, "", "");
}
}
///
/// Returns the type of file system element.
///
public FileSystemType Type
{
get
{
return FileSystemType.Directory;
}
}
///
/// Creates the directory in S3. If no object key was specified when creating the S3DirectoryInfo then the bucket will be created.
///
///
///
public void Create()
{
bool bucketExists;
if (!ExistsWithBucketCheck(out bucketExists))
{
if (String.IsNullOrEmpty(key))
{
var request = new PutBucketRequest
{
BucketName = bucket
};
((Amazon.Runtime.Internal.IAmazonWebServiceRequest)request).AddBeforeRequestHandler(S3Helper.FileIORequestEventHandler);
s3Client.PutBucket(request);
WaitTillBucketS3StateIsConsistent(true);
}
else
{
if (!bucketExists)
{
var request = new PutBucketRequest
{
BucketName = bucket
};
((Amazon.Runtime.Internal.IAmazonWebServiceRequest)request).AddBeforeRequestHandler(S3Helper.FileIORequestEventHandler);
s3Client.PutBucket(request);
WaitTillBucketS3StateIsConsistent(true);
}
var putObjectRequest = new PutObjectRequest
{
BucketName = bucket,
Key = S3Helper.EncodeKey(key),
InputStream = new MemoryStream()
};
((Amazon.Runtime.Internal.IAmazonWebServiceRequest)putObjectRequest).AddBeforeRequestHandler(S3Helper.FileIORequestEventHandler);
s3Client.PutObject(putObjectRequest);
}
}
}
///
/// Creates a sub directory inside the instance of S3DirectoryInfo.
///
///
///
///
///
public S3DirectoryInfo CreateSubdirectory(string directory)
{
S3DirectoryInfo ret = null;
ret = GetDirectory(directory);
ret.Create();
return ret;
}
///
/// Deletes all the files in this directory as well as this directory.
///
///
///
///
public void Delete()
{
Delete(false);
}
///
/// Deletes all the files in this directory as well as this directory. If recursive is set to true then all sub directories will be
/// deleted as well.
///
///
///
///
/// If true then sub directories will be deleted as well.
public void Delete(bool recursive)
{
if (String.IsNullOrEmpty(bucket))
{
throw new NotSupportedException();
}
if (recursive)
{
ListObjectsRequest listRequest = new ListObjectsRequest
{
BucketName = bucket,
Prefix = S3Helper.EncodeKey(this.key)
};
((Amazon.Runtime.Internal.IAmazonWebServiceRequest)listRequest).AddBeforeRequestHandler(S3Helper.FileIORequestEventHandler);
DeleteObjectsRequest deleteRequest = new DeleteObjectsRequest
{
BucketName = bucket
};
((Amazon.Runtime.Internal.IAmazonWebServiceRequest)deleteRequest).AddBeforeRequestHandler(S3Helper.FileIORequestEventHandler);
ListObjectsResponse listResponse = null;
do
{
listResponse = s3Client.ListObjects(listRequest);
// Sort to make sure the Marker for paging is set to the last lexiographical key.
foreach (S3Object s3o in listResponse.S3Objects.OrderBy(x => x.Key))
{
deleteRequest.AddKey(s3o.Key);
if (deleteRequest.Objects.Count == Util.S3Constants.MULTIPLE_OBJECT_DELETE_LIMIT)
{
s3Client.DeleteObjects(deleteRequest);
deleteRequest.Objects.Clear();
}
listRequest.Marker = s3o.Key;
}
} while (listResponse.IsTruncated);
if (deleteRequest.Objects.Count > 0)
{
s3Client.DeleteObjects(deleteRequest);
}
}
if (String.IsNullOrEmpty(key) && Exists)
{
var request = new DeleteBucketRequest { BucketName = bucket };
((Amazon.Runtime.Internal.IAmazonWebServiceRequest)request).AddBeforeRequestHandler(S3Helper.FileIORequestEventHandler);
s3Client.DeleteBucket(request);
WaitTillBucketS3StateIsConsistent(false);
}
else
{
if (!EnumerateFileSystemInfos().GetEnumerator().MoveNext() && Exists)
{
var request = new DeleteObjectRequest { BucketName = bucket, Key = S3Helper.EncodeKey(key) };
((Amazon.Runtime.Internal.IAmazonWebServiceRequest)request).AddBeforeRequestHandler(S3Helper.FileIORequestEventHandler);
s3Client.DeleteObject(request);
Parent.Create();
}
}
}
///
/// Enumerate the sub directories of this directory.
///
///
///
/// An enumerable collection of directories.
public IEnumerable EnumerateDirectories()
{
return EnumerateDirectories("*",SearchOption.TopDirectoryOnly);
}
///
/// Enumerate the sub directories of this directory.
///
/// The search string. The default pattern is "*", which returns all directories.
///
///
/// An enumerable collection of directories that matches searchPattern.
public IEnumerable EnumerateDirectories(string searchPattern)
{
return EnumerateDirectories(searchPattern, SearchOption.TopDirectoryOnly);
}
///
/// Enumerate the sub directories of this directory.
///
/// The search string. The default pattern is "*", which returns all directories.
/// One of the enumeration values that specifies whether the search operation should include only the current directory or all subdirectories. The default value is TopDirectoryOnly.
///
///
/// An enumerable collection of directories that matches searchPattern and searchOption.
public IEnumerable EnumerateDirectories(string searchPattern, SearchOption searchOption)
{
IEnumerable folders = null;
if (String.IsNullOrEmpty(bucket))
{
var request = new ListBucketsRequest();
((Amazon.Runtime.Internal.IAmazonWebServiceRequest)request).AddBeforeRequestHandler(S3Helper.FileIORequestEventHandler);
folders = s3Client.ListBuckets(request).Buckets
.ConvertAll(s3Bucket => new S3DirectoryInfo(s3Client,s3Bucket.BucketName,""));
}
else
{
var request = new ListObjectsRequest
{
BucketName = bucket,
Delimiter = "/",
Prefix = S3Helper.EncodeKey(key)
};
((Amazon.Runtime.Internal.IAmazonWebServiceRequest)request).AddBeforeRequestHandler(S3Helper.FileIORequestEventHandler);
folders = new EnumerableConverter
((IEnumerable)(PaginatedResourceFactory.Create(new PaginatedResourceInfo()
.WithClient(s3Client)
.WithMethodName("ListObjects")
.WithRequest(request)
.WithItemListPropertyPath("CommonPrefixes")
.WithTokenRequestPropertyPath("Marker")
.WithTokenResponsePropertyPath("NextMarker"))),
prefix => new S3DirectoryInfo(s3Client, bucket, S3Helper.DecodeKey(prefix)));
}
//handle if recursion is set
if (searchOption == SearchOption.AllDirectories)
{
IEnumerable foldersToAdd = new List();
foreach (S3DirectoryInfo dir in folders)
{
foldersToAdd = foldersToAdd.Concat(dir.EnumerateDirectories(searchPattern, searchOption));
}
folders = folders.Concat(foldersToAdd);
}
//filter based on search pattern
var regEx = WildcardToRegex(searchPattern);
folders = folders.Where(s3dirInfo => Regex.IsMatch(s3dirInfo.Name, regEx, RegexOptions.IgnoreCase));
return folders;
}
///
/// Enumerate the files of this directory.
///
///
///
/// An enumerable collection of files.
public IEnumerable EnumerateFiles()
{
return EnumerateFiles("*", SearchOption.TopDirectoryOnly);
}
///
/// Enumerate the sub directories of this directory.
///
/// The search string. The default pattern is "*", which returns all files.
///
///
/// An enumerable collection of files that matches searchPattern.
public IEnumerable EnumerateFiles(string searchPattern)
{
return EnumerateFiles(searchPattern, SearchOption.TopDirectoryOnly);
}
///
/// Enumerate the files of this directory.
///
/// The search string. The default pattern is "*", which returns all files.
/// One of the enumeration values that specifies whether the search operation should include only the current directory or all subdirectories. The default value is TopDirectoryOnly.
///
///
/// An enumerable collection of files that matches searchPattern and searchOption.
public IEnumerable EnumerateFiles(string searchPattern, SearchOption searchOption)
{
IEnumerable files = null;
if (String.IsNullOrEmpty(bucket))
{
files = new List();
}
else
{
var request = new ListObjectsRequest
{
BucketName = bucket,
Delimiter = "/",
Prefix = S3Helper.EncodeKey(key)
};
((Amazon.Runtime.Internal.IAmazonWebServiceRequest)request).AddBeforeRequestHandler(S3Helper.FileIORequestEventHandler);
files = new EnumerableConverter
(((IEnumerable)(PaginatedResourceFactory.Create(new PaginatedResourceInfo()
.WithClient(s3Client)
.WithMethodName("ListObjects")
.WithRequest(request)
.WithItemListPropertyPath("S3Objects")
.WithTokenRequestPropertyPath("Marker")
.WithTokenResponsePropertyPath("NextMarker"))))
.Where(s3Object =>
{
string decodeKey = S3Helper.DecodeKey(s3Object.Key);
return !String.Equals(decodeKey, key, StringComparison.Ordinal) && !decodeKey.EndsWith("\\", StringComparison.Ordinal);
}),
s3Object => new S3FileInfo(s3Client, bucket, S3Helper.DecodeKey(s3Object.Key)));
}
//handle if recursion is set
if (searchOption == SearchOption.AllDirectories)
{
IEnumerable foldersToSearch = EnumerateDirectories();
foreach (S3DirectoryInfo dir in foldersToSearch)
{
files = files.Concat(dir.EnumerateFiles(searchPattern, searchOption));
}
}
//filter based on search pattern
var regEx = WildcardToRegex(searchPattern);
files = files.Where(s3fileInfo => Regex.IsMatch(s3fileInfo.Name, regEx, RegexOptions.IgnoreCase));
return files;
}
///
/// Enumerate the files of this directory.
///
///
///
/// An enumerable collection of files.
public IEnumerable EnumerateFileSystemInfos()
{
return EnumerateFileSystemInfos("*", SearchOption.TopDirectoryOnly);
}
///
/// Enumerate the files of this directory.
///
/// The search string. The default pattern is "*", which returns all files.
///
///
/// An enumerable collection of files that matches searchPattern.
public IEnumerable EnumerateFileSystemInfos(string searchPattern)
{
return EnumerateFileSystemInfos(searchPattern, SearchOption.TopDirectoryOnly);
}
///
/// Enumerate the files of this directory.
///
/// The search string. The default pattern is "*", which returns all files.
/// One of the enumeration values that specifies whether the search operation should include only the current directory or all subdirectories. The default value is TopDirectoryOnly.
///
///
/// An enumerable collection of files that matches searchPattern and searchOption.
public IEnumerable EnumerateFileSystemInfos(string searchPattern, SearchOption searchOption)
{
IEnumerable files = EnumerateFiles(searchPattern, searchOption).Cast();
IEnumerable folders = EnumerateDirectories(searchPattern, searchOption).Cast();
return files.Concat(folders);
}
///
/// Returns the S3DirectoryInfo for the specified sub directory.
///
/// Directory to get the S3DirectroyInfo for.
/// The S3DirectoryInfo for the specified sub directory.
public S3DirectoryInfo GetDirectory(string directory)
{
S3DirectoryInfo ret = null;
if (String.IsNullOrEmpty(bucket))
{
ret = new S3DirectoryInfo(s3Client, directory, "");
}
else
{
ret = new S3DirectoryInfo(s3Client, bucket, string.Format(CultureInfo.InvariantCulture, "{0}{1}", key, directory));
}
return ret;
}
///
/// Returns an array of S3DirectoryInfos for the directories in this directory.
///
///
///
/// An array of directories.
public S3DirectoryInfo[] GetDirectories()
{
return GetDirectories("*", SearchOption.TopDirectoryOnly);
}
///
/// Returns an array of S3DirectoryInfos for the directories in this directory.
///
/// The search string. The default pattern is "*", which returns all files.
/// An array of files that matches searchPattern.
public S3DirectoryInfo[] GetDirectories(string searchPattern)
{
return GetDirectories(searchPattern, SearchOption.TopDirectoryOnly);
}
///
/// Returns an array of S3DirectoryInfos for the directories in this directory.
///
/// The search string. The default pattern is "*", which returns all directories.
/// One of the enumeration values that specifies whether the search operation should include only the current directory or all subdirectories. The default value is TopDirectoryOnly.
///
///
/// An array of directories that matches searchPattern and searchOption.
public S3DirectoryInfo[] GetDirectories(string searchPattern, SearchOption searchOption)
{
return EnumerateDirectories(searchPattern, searchOption).ToArray();
}
///
/// Returns the S3FileInfo for the specified file.
///
/// File to get the S3FileInfo for.
/// The S3FileInfo for the specified file.
public S3FileInfo GetFile(string filename)
{
S3FileInfo ret = null;
if (String.IsNullOrEmpty(bucket))
{
throw new NotSupportedException("Cannot create files in the root of s3, you need a bucket");
}
else
{
ret = new S3FileInfo(s3Client, bucket, string.Format(CultureInfo.InvariantCulture, "{0}{1}", key, filename));
}
return ret;
}
///
/// Returns an array of S3FileInfos for the files in this directory.
///
///
///
/// An array of files.
public S3FileInfo[] GetFiles()
{
return GetFiles("*", SearchOption.TopDirectoryOnly);
}
///
/// Returns an array of S3FileInfos for the files in this directory.
///
/// The search string. The default pattern is "*", which returns all files.
///
///
/// An array of files that matches searchPattern.
public S3FileInfo[] GetFiles(string searchPattern)
{
return GetFiles(searchPattern, SearchOption.TopDirectoryOnly);
}
///
/// Returns an array of S3FileInfos for the files in this directory.
///
/// The search string. The default pattern is "*", which returns all files.
/// One of the enumeration values that specifies whether the search operation should include only the current directory or all subdirectories. The default value is TopDirectoryOnly.
///
///
/// An array of files that matches searchPattern and searchOption.
public S3FileInfo[] GetFiles(string searchPattern, SearchOption searchOption)
{
return EnumerateFiles(searchPattern, searchOption).ToArray();
}
///
/// Returns an array of IS3FileSystemInfos for the files in this directory.
///
///
///
/// An array of files.
public IS3FileSystemInfo[] GetFileSystemInfos()
{
return GetFileSystemInfos("*",SearchOption.TopDirectoryOnly);
}
///
/// Returns an array of IS3FileSystemInfos for the files in this directory.
///
/// The search string. The default pattern is "*", which returns all files.
///
///
/// An array of files that matches searchPattern.
public IS3FileSystemInfo[] GetFileSystemInfos(string searchPattern)
{
return GetFileSystemInfos(searchPattern, SearchOption.TopDirectoryOnly);
}
///
/// Returns an array of IS3FileSystemInfos for the files in this directory.
///
/// The search string. The default pattern is "*", which returns all files.
/// One of the enumeration values that specifies whether the search operation should include only the current directory or all subdirectories. The default value is TopDirectoryOnly.
///
///
/// An array of files that matches searchPattern and searchOption.
public IS3FileSystemInfo[] GetFileSystemInfos(string searchPattern, SearchOption searchOption)
{
return EnumerateFileSystemInfos(searchPattern, searchOption).ToArray();
}
///
/// Copies the files from this directory to the target directory specified by the bucket and object key.
///
/// The target bucket to copy to.
/// The target object key which represents a directory in S3.
///
///
///
/// S3DirectoryInfo for the target location.
public S3DirectoryInfo CopyTo(string newBucket, string newKey)
{
return CopyTo(newBucket, newKey, DateTime.MinValue);
}
///
/// Copies the files from this directory to the target directory specified by the bucket and object key. Only
/// files that have changed since the changeSince date will be copied.
///
/// The target bucket to copy to.
/// The target object key which represents a directory in S3.
/// Date which files must have changed since in ordered to be copied.
///
///
///
/// S3DirectoryInfo for the target location.
public S3DirectoryInfo CopyTo(string newBucket, string newKey, DateTime changesSince)
{
S3DirectoryInfo newLoc = new S3DirectoryInfo(s3Client, newBucket, newKey);
return CopyTo(newLoc, changesSince);
}
///
/// Copies the files from this directory to the target directory.
///
/// The target directory to copy to.
///
///
///
/// S3DirectoryInfo for the target location.
public S3DirectoryInfo CopyTo(S3DirectoryInfo newLoc)
{
return CopyTo(newLoc, DateTime.MinValue);
}
///
/// Copies the files from this directory to the target directory. Only
/// files that have changed since the changeSince date will be copied.
///
/// The target directory to copy to.
/// Date which files must have changed since in ordered to be copied.
///
///
///
/// S3DirectoryInfo for the target location.
public S3DirectoryInfo CopyTo(S3DirectoryInfo newLoc, DateTime changesSince)
{
if (!newLoc.Exists)
{
throw new ArgumentException("Destination for CopyTo operation does not exist.", "newLoc");
}
if (LastWriteTime > changesSince)
{
foreach (S3DirectoryInfo dir in EnumerateDirectories())
{
S3DirectoryInfo subLoc = newLoc.CreateSubdirectory(dir.Name);
dir.CopyTo(subLoc, changesSince);
}
foreach (S3FileInfo file in EnumerateFiles())
{
if (file.LastWriteTime > changesSince)
{
file.CopyTo(newLoc);
}
}
}
return newLoc;
}
///
/// Copies the files from the S3 directory to the local file system in the location indicated by the path parameter.
///
/// The location on the local file system to copy the files to.
///
///
/// DirectoryInfo for the local directory where files are copied to.
public DirectoryInfo CopyToLocal(string path)
{
return CopyToLocal(path, DateTime.MinValue);
}
///
/// Copies the files from the S3 directory to the local file system in the location indicated by the path parameter.
/// Only files that have been modified since the changesSince property will be copied.
///
/// The location on the local file system to copy the files to.
/// Date which files must have changed since in ordered to be copied.
///
///
///
/// DirectoryInfo for the local directory where files are copied to.
public DirectoryInfo CopyToLocal(string path, DateTime changesSince)
{
if (!Directory.Exists(path))
{
throw new ArgumentException("Destination for CopyTo operation does not exist.", "path");
}
if (LastWriteTime > changesSince)
{
foreach (S3DirectoryInfo dir in EnumerateDirectories())
{
var dirPath = string.Format(CultureInfo.InvariantCulture, "{0}\\{1}", path, dir.Name);
//Ensure the directory is a rooted within path. Otherwise error.
if (!InternalSDKUtils.IsFilePathRootedWithDirectoryPath(dirPath, path))
{
throw new AmazonClientException($"The directory {dirPath} is not allowed outside of the target directory {path}.");
}
DirectoryInfo newLoc = Directory.CreateDirectory(dirPath);
dir.CopyToLocal(newLoc.FullName, changesSince);
}
foreach (S3FileInfo file in EnumerateFiles())
{
if (file.LastWriteTime > changesSince)
{
file.CopyToLocal(string.Format(CultureInfo.InvariantCulture, "{0}\\{1}", path, file.Name));
}
}
}
return new DirectoryInfo(path);
}
///
/// Copies files from the local file system to S3 in this directory. Sub directories are copied as well.
///
/// The local file system path where the files are to be copied.
///
///
///
/// S3DirectoryInfo where the files are copied to.
public S3DirectoryInfo CopyFromLocal(string path)
{
return CopyFromLocal(path, DateTime.MinValue);
}
///
/// Copies files from the local file system to S3 in this directory. Sub directories are copied as well.
/// Only files that have been modified since the changesSince property will be copied.
///
/// The local file system path where the files are to copy.
/// Date which files must have changed since in ordered to be copied.
///
///
///
/// S3DirectoryInfo where the files are copied to.
public S3DirectoryInfo CopyFromLocal(string path, DateTime changesSince)
{
DirectoryInfo sourceLoc = new DirectoryInfo(path);
if (!sourceLoc.Exists)
{
throw new ArgumentException("Source for CopyFrom operation does not exist.", "path");
}
sourceLoc.Refresh();
if (sourceLoc.LastWriteTime > changesSince)
{
foreach (DirectoryInfo dir in sourceLoc.GetDirectories())
{
S3DirectoryInfo newLoc = CreateSubdirectory(dir.Name);
newLoc.CopyFromLocal(dir.FullName, changesSince);
}
foreach (FileInfo file in sourceLoc.GetFiles())
{
file.Refresh();
if (file.LastWriteTime > changesSince)
{
this.GetFile(file.Name).CopyFromLocal(file.FullName);
}
}
}
return this;
}
///
/// Moves the directory to the target directory specified by the bucket and object key.
///
/// The target bucket to move to.
/// The target object key which represents a directory in S3.
///
///
///
/// S3DirectoryInfo for the target location.
public S3DirectoryInfo MoveTo(string newBucket, string newKey)
{
S3DirectoryInfo newLoc = new S3DirectoryInfo(s3Client, newBucket, newKey);
return MoveTo(newLoc);
}
///
/// Moves the directory to the target S3 directory.
///
/// The target directory to copy to.
///
///
///
/// S3DirectoryInfo for the target location.
public S3DirectoryInfo MoveTo(S3DirectoryInfo newLoc)
{
if (!newLoc.Exists)
{
throw new ArgumentException("Destination for MoveTo operation does not exist.", "newLoc");
}
S3DirectoryInfo newSub = newLoc.CreateSubdirectory(Name);
foreach (S3DirectoryInfo dir in EnumerateDirectories())
{
dir.MoveTo(newSub);
}
foreach (S3FileInfo file in EnumerateFiles())
{
file.MoveTo(newSub);
}
Delete();
return newLoc;
}
///
/// Moves the files from the S3 directory to the local file system in the location indicated by the path parameter.
///
/// The location on the local file system to move the files to.
///
///
///
/// DirectoryInfo for the local directory where files are moved to.
public DirectoryInfo MoveToLocal(string path)
{
if (!Directory.Exists(path))
{
throw new ArgumentException("Destination for MoveTo operation does not exist.", "path");
}
foreach (S3DirectoryInfo dir in EnumerateDirectories())
{
var dirPath = string.Format(CultureInfo.InvariantCulture, "{0}\\{1}", path, dir.Name);
//Ensure the directory is a rooted within path. Otherwise error.
if (!InternalSDKUtils.IsFilePathRootedWithDirectoryPath(dirPath, path))
{
throw new AmazonClientException($"The directory {dirPath} is not allowed outside of the target directory {path}.");
}
DirectoryInfo newLoc = Directory.CreateDirectory(dirPath);
dir.MoveToLocal(newLoc.FullName);
}
foreach (S3FileInfo file in EnumerateFiles())
{
file.MoveToLocal(string.Format(CultureInfo.InvariantCulture, "{0}\\{1}", path, file.Name));
}
Delete();
return new DirectoryInfo(path);
}
///
/// Moves files from the local file system to S3 in this directory. Sub directories are moved as well.
///
/// The local file system path where the files are to be moved.
///
///
///
/// S3DirectoryInfo where the files are moved to.
public S3DirectoryInfo MoveFromLocal(string path)
{
DirectoryInfo sourceLoc = new DirectoryInfo(path);
if (!sourceLoc.Exists)
{
throw new ArgumentException("Source for MoveFrom operation does not exist.", "path");
}
foreach (DirectoryInfo dir in sourceLoc.GetDirectories())
{
S3DirectoryInfo newLoc = CreateSubdirectory(dir.Name);
newLoc.MoveFromLocal(dir.FullName);
}
foreach (FileInfo file in sourceLoc.GetFiles())
{
this.GetFile(file.Name).MoveFromLocal(file.FullName);
}
sourceLoc.Delete();
return this;
}
///
/// Implement the ToString method.
///
///
public override string ToString()
{
return FullName;
}
static string WildcardToRegex(string pattern)
{
string newPattern = Regex.Escape(pattern).
Replace("\\*", ".*").
Replace("\\?", ".");
return "^" + newPattern + "$";
}
///
/// Creating and deleting buckets can sometimes take a little bit of time. To make sure
/// users of this API do not experience the side effects of the eventual consistency
/// we block until the state change has happened.
///
///
void WaitTillBucketS3StateIsConsistent(bool exists)
{
int success = 0;
bool currentState = false;
var start = this.S3Client.Config.CorrectedUtcNow;
do
{
var buckets = this.S3Client.ListBuckets().Buckets;
currentState = buckets.FirstOrDefault(x => string.Equals(x.BucketName, this.BucketName)) != null;
if (currentState == exists)
{
success++;
if (success == EVENTUAL_CONSISTENCY_SUCCESS_IN_ROW)
break;
}
else
{
success = 0;
}
Thread.Sleep(EVENTUAL_CONSISTENCY_POLLING_PERIOD);
} while ((this.S3Client.Config.CorrectedUtcNow - start).TotalMilliseconds < EVENTUAL_CONSISTENCY_MAX_WAIT) ;
}
}
internal class EnumerableConverter : IEnumerable
{
internal IEnumerable baseEnum = null;
internal Func converter = null;
internal EnumerableConverter(IEnumerable start, Func convert)
{
baseEnum = start;
converter = convert;
}
IEnumerator IEnumerable.GetEnumerator()
{
return (IEnumerator)GetEnumerator();
}
public IEnumerator GetEnumerator()
{
return new ConvertingEnumerator(this);
}
}
internal class ConvertingEnumerator : IEnumerator
{
Func convert;
IEnumerator getT;
bool isConverted = false;
U convertedCurrent = default(U);
internal ConvertingEnumerator(EnumerableConverter ec)
{
getT = ec.baseEnum.GetEnumerator();
convert = ec.converter;
}
public bool MoveNext()
{
isConverted = false;
convertedCurrent = default(U);
return getT.MoveNext();
}
public void Reset()
{
isConverted = false;
convertedCurrent = default(U);
getT.Reset();
}
object IEnumerator.Current
{
get
{
return Current;
}
}
public U Current
{
get
{
if (!isConverted)
{
convertedCurrent = convert(getT.Current);
isConverted = true;
}
return convertedCurrent;
}
}
public void Dispose()
{
}
}
}