/*******************************************************************************
* 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.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization;
using System.Security.AccessControl;
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.Util;
using System.Globalization;
namespace Amazon.S3.IO
{
///
/// Mimics the System.IO.FileInfo for a file in S3. It exposes properties and methods manipulating files in S3.
///
public sealed class S3FileInfo : IS3FileSystemInfo
{
private IAmazonS3 s3Client;
private string bucket;
private string key;
///
/// Initialize a new instance of the S3FileInfo 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 S3FileInfo(IAmazonS3 s3Client, string bucket, string key)
{
if (s3Client == null)
{
throw new ArgumentNullException("s3Client");
}
if (String.IsNullOrEmpty(bucket))
{
throw new ArgumentNullException("bucket");
}
if (String.IsNullOrEmpty(key) || String.Equals(key, "\\"))
{
throw new ArgumentNullException("key");
}
if (key.EndsWith("\\", StringComparison.Ordinal))
{
throw new ArgumentException("key is a directory name");
}
this.s3Client = s3Client;
this.bucket = bucket;
this.key = key;
}
///
/// Returns the parent S3DirectoryInfo.
///
public S3DirectoryInfo Directory
{
get
{
int index = key.LastIndexOf('\\');
string directoryName = null;
if (index >= 0)
directoryName = key.Substring(0, index);
return new S3DirectoryInfo(s3Client, bucket, directoryName);
}
}
///
/// The full name of parent directory.
///
public string DirectoryName
{
get
{
return Directory.FullName;
}
}
///
/// Checks S3 if the file exists in S3 and return true if it does.
///
///
///
public bool Exists
{
get
{
bool bucketExists;
return ExistsWithBucketCheck(out bucketExists);
}
}
internal bool ExistsWithBucketCheck(out bool bucketExists)
{
bucketExists = true;
try
{
var request = new GetObjectMetadataRequest
{
BucketName = bucket,
Key = S3Helper.EncodeKey(key)
};
((Amazon.Runtime.Internal.IAmazonWebServiceRequest)request).AddBeforeRequestHandler(S3Helper.FileIORequestEventHandler);
// If the object doesn't exist then a "NotFound" will be thrown
s3Client.GetObjectMetadata(request);
return true;
}
catch (AmazonS3Exception e)
{
if (string.Equals(e.ErrorCode, "NoSuchBucket"))
{
bucketExists = false;
return false;
}
else if (string.Equals(e.ErrorCode, "NotFound"))
{
return false;
}
throw;
}
}
///
/// Gets the file's extension.
///
public string Extension
{
get
{
int index = Name.LastIndexOf('.');
if (index == -1 || this.Name.Length <= (index + 1))
return null;
return this.Name.Substring(index + 1);
}
}
///
/// Returns the full path including the bucket.
///
public string FullName
{
get
{
return string.Format(CultureInfo.InvariantCulture, "{0}:\\{1}", bucket, key);
}
}
///
/// Returns the last time the file was modified.
///
///
///
public DateTime LastWriteTime
{
get
{
DateTime ret = DateTime.MinValue;
if (Exists)
{
//ret = s3Client.GetObjectMetadata(new GetObjectMetadataRequest()
// .WithBucketName(bucket)
// .WithKey(S3Helper.EncodeKey(key))
// .WithBeforeRequestHandler(S3Helper.FileIORequestEventHandler) as GetObjectMetadataRequest)
// .LastModified.ToLocalTime();
var request = new GetObjectMetadataRequest
{
BucketName = bucket,
Key = S3Helper.EncodeKey(key),
};
((Amazon.Runtime.Internal.IAmazonWebServiceRequest)request).AddBeforeRequestHandler(S3Helper.FileIORequestEventHandler);
var response = s3Client.GetObjectMetadata(request);
ret = response.LastModified.ToLocalTime();
}
return ret;
}
}
///
/// Returns the last time the file was modified in UTC.
///
///
///
public DateTime LastWriteTimeUtc
{
get
{
DateTime ret = DateTime.MinValue;
if (Exists)
{
//ret = s3Client.GetObjectMetadata(new GetObjectMetadataRequest()
// .WithBucketName(bucket)
// .WithKey(S3Helper.EncodeKey(key))
// .WithBeforeRequestHandler(S3Helper.FileIORequestEventHandler) as GetObjectMetadataRequest)
// .LastModified;
var request = new GetObjectMetadataRequest
{
BucketName = bucket,
Key = S3Helper.EncodeKey(key),
};
((Amazon.Runtime.Internal.IAmazonWebServiceRequest)request).AddBeforeRequestHandler(S3Helper.FileIORequestEventHandler);
var response = s3Client.GetObjectMetadata(request);
ret = response.LastModified;
}
return ret;
}
}
///
/// Returns the content length of the file.
///
///
///
public long Length
{
get
{
long ret = 0;
if (Exists)
{
//ret = s3Client.GetObjectMetadata(new GetObjectMetadataRequest()
// .WithBucketName(bucket)
// .WithKey(S3Helper.EncodeKey(key))
// .WithBeforeRequestHandler(S3Helper.FileIORequestEventHandler) as GetObjectMetadataRequest)
// .ContentLength;
var request = new GetObjectMetadataRequest
{
BucketName = bucket,
Key = S3Helper.EncodeKey(key),
};
((Amazon.Runtime.Internal.IAmazonWebServiceRequest)request).AddBeforeRequestHandler(S3Helper.FileIORequestEventHandler);
var response = s3Client.GetObjectMetadata(request);
ret = response.ContentLength;
}
return ret;
}
}
///
/// Returns the file name without its directory name.
///
public string Name
{
get
{
int index = key.LastIndexOf('\\');
return key.Substring(index + 1);
}
}
///
/// Returns the type of file system element.
///
public FileSystemType Type
{
get
{
return FileSystemType.File;
}
}
///
/// Copies this file's content to the file indicated by the S3 bucket and object key.
/// If the file already exists in S3 than an ArgumentException is thrown.
///
/// S3 bucket to copy the file to.
/// S3 object key to copy the file to.
///
///
///
/// S3FileInfo of the newly copied file.
public S3FileInfo CopyTo(string newBucket, string newKey)
{
return CopyTo(newBucket, newKey, false);
}
///
/// Copies this file's content to the file indicated by the S3 bucket and object key.
/// If the file already exists in S3 and overwrite is set to false than an ArgumentException is thrown.
///
/// S3 bucket to copy the file to.
/// S3 object key to copy the file to.
/// Determines whether the file can be overwritten.
/// If the file already exists in S3 and overwrite is set to false.
///
///
///
/// S3FileInfo of the newly copied file.
public S3FileInfo CopyTo(string newBucket, string newKey, bool overwrite)
{
if (String.IsNullOrEmpty(newBucket))
{
throw new ArgumentException("A bucket is required to copy an object","newBucket");
}
S3FileInfo ret = null;
if (String.IsNullOrEmpty(newKey) || newKey.EndsWith("\\", StringComparison.Ordinal))
{
ret = CopyTo(new S3DirectoryInfo(s3Client, newBucket, newKey), overwrite);
}
else
{
ret = CopyTo(new S3FileInfo(s3Client, newBucket, newKey), overwrite);
}
return ret;
}
///
/// Copies this file to the target directory.
/// If the file already exists in S3 than an ArgumentException is thrown.
///
/// Target directory where to copy the file to.
/// If the directory does not exist.
/// If the file already exists in S3.
///
///
/// S3FileInfo of the newly copied file.
public S3FileInfo CopyTo(S3DirectoryInfo dir)
{
return CopyTo(dir, false);
}
///
/// Copies this file to the target directory.
/// If the file already exists in S3 and overwrite is set to false than an ArgumentException is thrown.
///
/// Target directory where to copy the file to.
/// Determines whether the file can be overwritten.
/// If the directory does not exist.
/// If the file already exists in S3 and overwrite is set to false.
///
///
/// S3FileInfo of the newly copied file.
public S3FileInfo CopyTo(S3DirectoryInfo dir, bool overwrite)
{
if (!dir.Exists)
{
throw new ArgumentException("directory does not exist", "dir");
}
S3FileInfo ret = CopyTo(new S3FileInfo(dir.S3Client, dir.BucketName, string.Format(CultureInfo.InvariantCulture, "{0}{1}", dir.ObjectKey, Name)),overwrite);
return ret;
}
///
/// Copies this file to the location indicated by the passed in S3FileInfo.
/// If the file already exists in S3 than an ArgumentException is thrown.
///
/// The target location to copy this file to.
/// If the file already exists in S3.
///
///
/// S3FileInfo of the newly copied file.
public S3FileInfo CopyTo(S3FileInfo file)
{
return CopyTo(file, false);
}
///
/// Copies this file to the location indicated by the passed in S3FileInfo.
/// If the file already exists in S3 and overwrite is set to false than an ArgumentException is thrown.
///
/// The target location to copy this file to.
/// Determines whether the file can be overwritten.
/// If the file already exists in S3 and overwrite is set to false.
///
///
/// S3FileInfo of the newly copied file.
public S3FileInfo CopyTo(S3FileInfo file, bool overwrite)
{
if (!overwrite)
{
if (file.Exists)
{
throw new IOException("File already exists");
}
}
if (SameClient(file))
{
var request = new CopyObjectRequest
{
DestinationBucket = file.BucketName,
DestinationKey = S3Helper.EncodeKey(file.ObjectKey),
SourceBucket = bucket,
SourceKey = S3Helper.EncodeKey(key)
};
((Amazon.Runtime.Internal.IAmazonWebServiceRequest)request).AddBeforeRequestHandler(S3Helper.FileIORequestEventHandler);
s3Client.CopyObject(request);
}
else
{
var getObjectRequest = new GetObjectRequest
{
BucketName = bucket,
Key = S3Helper.EncodeKey(key)
};
((Amazon.Runtime.Internal.IAmazonWebServiceRequest)getObjectRequest).AddBeforeRequestHandler(S3Helper.FileIORequestEventHandler);
var getObjectResponse = s3Client.GetObject(getObjectRequest);
using (Stream stream = getObjectResponse.ResponseStream)
{
var putObjectRequest = new PutObjectRequest
{
BucketName = file.BucketName,
Key = S3Helper.EncodeKey(file.ObjectKey),
InputStream = stream
};
((Amazon.Runtime.Internal.IAmazonWebServiceRequest)putObjectRequest).AddBeforeRequestHandler(S3Helper.FileIORequestEventHandler);
file.S3Client.PutObject(putObjectRequest);
}
}
return file;
}
///
/// Copies from S3 to the local file system.
/// If the file already exists on the local file system than an ArgumentException is thrown.
///
/// The path where to copy the file to.
/// If the file already exists locally.
///
///
/// FileInfo for the local file where file is copied to.
public FileInfo CopyToLocal(string destFileName)
{
return CopyToLocal(destFileName, false);
}
///
/// Copies from S3 to the local file system.
/// If the file already exists on the local file system and overwrite is set to false than an ArgumentException is thrown.
///
/// The path where to copy the file to.
/// Determines whether the file can be overwritten.
/// If the file already exists locally and overwrite is set to false.
///
///
/// FileInfo for the local file where file is copied to.
public FileInfo CopyToLocal(string destFileName, bool overwrite)
{
if (!overwrite)
{
if (new FileInfo(destFileName).Exists)
{
throw new IOException("File already exists");
}
}
var getObjectRequest = new GetObjectRequest
{
BucketName = bucket,
Key = S3Helper.EncodeKey(key)
};
((Amazon.Runtime.Internal.IAmazonWebServiceRequest)getObjectRequest).AddBeforeRequestHandler(S3Helper.FileIORequestEventHandler);
using (var getObjectResponse = s3Client.GetObject(getObjectRequest))
{
getObjectResponse.WriteResponseStreamToFile(destFileName);
}
return new FileInfo(destFileName);
}
///
/// Copies the file from the local file system to S3.
/// If the file already exists in S3 than an ArgumentException is thrown.
///
/// Location of the file on the local file system to copy.
/// If the file already exists in S3.
///
///
/// S3FileInfo where the file is copied to.
public S3FileInfo CopyFromLocal(string srcFileName)
{
return CopyFromLocal(srcFileName, false);
}
///
/// Copies the file from the local file system to S3.
/// If the file already exists in S3 and overwrite is set to false than an ArgumentException is thrown.
///
/// Location of the file on the local file system to copy.
/// Determines whether the file can be overwritten.
/// If the file already exists in S3 and overwrite is set to false.
///
///
/// S3FileInfo where the file is copied to.
public S3FileInfo CopyFromLocal(string srcFileName, bool overwrite)
{
if (!overwrite)
{
if (Exists)
{
throw new IOException("File already exists");
}
}
var putObjectRequest = new PutObjectRequest
{
BucketName = bucket,
Key = S3Helper.EncodeKey(key),
FilePath = srcFileName
};
((Amazon.Runtime.Internal.IAmazonWebServiceRequest)putObjectRequest).AddBeforeRequestHandler(S3Helper.FileIORequestEventHandler);
s3Client.PutObject(putObjectRequest);
return this;
}
///
/// Returns a Stream that can be used to write data to S3.
///
///
/// The content will not be written to S3 until the Stream is closed.
///
///
///
/// Stream to write content to.
public Stream Create()
{
return new S3FileStream(this.S3Client, this.BucketName, this.ObjectKey, FileMode.Create, FileAccess.Write);
}
///
/// Returns a StreamWriter that can be used to write data to S3.
///
///
/// The content will not be written to S3 until the Stream is closed.
///
///
///
/// Stream to write content to.
public StreamWriter CreateText()
{
return new StreamWriter(Create());
}
///
/// Deletes the from S3.
///
///
///
public void Delete()
{
if (Exists)
{
var deleteObjectRequest = new DeleteObjectRequest
{
BucketName = bucket,
Key = S3Helper.EncodeKey(key)
};
((Amazon.Runtime.Internal.IAmazonWebServiceRequest)deleteObjectRequest).AddBeforeRequestHandler(S3Helper.FileIORequestEventHandler);
s3Client.DeleteObject(deleteObjectRequest);
Directory.Create();
}
}
///
/// Moves the file to a a new location in S3.
///
/// Bucket to move the file to.
/// Object key to move the file to.
/// If the file already exists in S3.
///
///
///
/// S3FileInfo for the target location.
public S3FileInfo MoveTo(string bucket, string key)
{
S3FileInfo ret = CopyTo(bucket,key,false);
Delete();
return ret;
}
///
/// Moves the file to a a new location in S3.
///
/// The target directory to copy to.
/// If the file already exists in S3.
///
///
///
/// S3FileInfo for the target location.
public S3FileInfo MoveTo(S3DirectoryInfo path)
{
S3FileInfo ret = CopyTo(path, false);
Delete();
return ret;
}
///
/// Moves the file to a a new location in S3.
///
/// The target file to copy to.
/// If the file already exists in S3.
///
///
///
/// S3FileInfo for the target location.
public S3FileInfo MoveTo(S3FileInfo file)
{
S3FileInfo ret = CopyTo(file, false);
Delete();
return ret;
}
///
/// Moves the file from S3 to the local file system in the location indicated by the path parameter.
///
/// The location on the local file system to move the file to.
/// If the file already exists locally.
///
///
///
/// FileInfo for the local file where file is moved to.
public FileInfo MoveToLocal(string path)
{
FileInfo ret = CopyToLocal(path, false);
Delete();
return ret;
}
///
/// Moves the file from the local file system to S3 in this directory.
/// If the file already exists in S3 than an ArgumentException is thrown.
///
/// The local file system path where the files are to be moved.
/// If the file already exists locally.
///
///
///
/// S3FileInfo where the file is moved to.
public S3FileInfo MoveFromLocal(string path)
{
S3FileInfo ret = CopyFromLocal(path, false);
File.Delete(path);
return ret;
}
///
/// Moves the file from the local file system to S3 in this directory.
/// If the file already exists in S3 and overwrite is set to false than an ArgumentException is thrown.
///
/// The local file system path where the files are to be moved.
/// Determines whether the file can be overwritten.
/// If the file already exists in S3 and overwrite is set to false.
///
///
///
/// S3FileInfo where the file is moved to.
public S3FileInfo MoveFromLocal(string path, bool overwrite)
{
S3FileInfo ret = CopyFromLocal(path, overwrite);
File.Delete(path);
return ret;
}
///
/// Returns a Stream for reading the contents of the file.
///
/// The file is already open.
///
///
/// Stream to read from.
public Stream OpenRead()
{
return new S3FileStream(this.S3Client, this.BucketName, this.ObjectKey, FileMode.Open, FileAccess.Read);
}
///
/// Returns a StreamReader for reading the contents of the file.
///
/// The file is already open.
///
///
/// StreamReader to read from.
public StreamReader OpenText()
{
return new StreamReader(OpenRead());
}
///
/// Returns a Stream for writing to S3. If the file already exists it will be overwritten.
///
///
/// The content will not be written to S3 until the Stream is closed.
///
/// The file is already open.
///
///
/// Stream to write from.
public Stream OpenWrite()
{
return new S3FileStream(this.S3Client, this.BucketName, this.ObjectKey, FileMode.OpenOrCreate, FileAccess.Write);
}
///
/// Replaces the destination file with the content of this file and then deletes the orignial file. If a backup location is specifed then the content of destination file is
/// backup to it.
///
/// Destination bucket of this file will be copy to.
/// Destination object key of this file will be copy to.
/// Backup bucket to store the contents of the destination file.
/// Backup object key to store the contents of the destination file.
///
///
///
///
/// S3FileInfo of the destination file.
public S3FileInfo Replace(string destinationBucket, string destinationKey, string backupBucket, string backupKey)
{
if (String.IsNullOrEmpty(destinationBucket))
{
throw new ArgumentException("A bucket is required to replace an object", "destinationBucket");
}
S3FileInfo destinationInfo;
if (String.IsNullOrEmpty(destinationKey))
{
destinationInfo = new S3FileInfo(this.s3Client, destinationBucket, this.Name);
}
else if (destinationKey.EndsWith("\\", StringComparison.Ordinal))
{
destinationInfo = new S3FileInfo(this.s3Client, destinationBucket, destinationKey + this.Name);
}
else
{
destinationInfo = new S3FileInfo(this.s3Client, destinationBucket, destinationKey);
}
S3FileInfo backupInfo = null;
if(!string.IsNullOrEmpty(backupBucket))
{
if (String.IsNullOrEmpty(backupKey))
{
backupInfo = new S3FileInfo(this.s3Client, backupBucket, this.Name);
}
else if (backupKey.EndsWith("\\", StringComparison.Ordinal))
{
backupInfo = new S3FileInfo(this.s3Client, backupBucket, backupKey + this.Name);
}
else
{
backupInfo = new S3FileInfo(this.s3Client, backupBucket, backupKey);
}
}
S3FileInfo ret = Replace(destinationInfo, backupInfo);
return ret;
}
///
/// Replaces the destination file with the content of this file and then deletes the orignial file. If a backupDir is specifed then the content of destination file is
/// backup to it.
///
/// Where the contents of this file will be copy to.
/// If specified the destFile is backup to it.
///
///
///
///
/// S3FileInfo of the destination file.
public S3FileInfo Replace(S3DirectoryInfo destDir, S3DirectoryInfo backupDir)
{
S3FileInfo ret = Replace(
new S3FileInfo(destDir.S3Client, destDir.BucketName, string.Format(CultureInfo.InvariantCulture, "{0}{1}", destDir.ObjectKey, Name)),
backupDir == null ? null : new S3FileInfo(backupDir.S3Client, backupDir.BucketName, string.Format(CultureInfo.InvariantCulture, "{0}{1}", backupDir.ObjectKey, Name)));
return ret;
}
///
/// Replaces the destination file with the content of this file and then deletes the orignial file. If a backupFile is specifed then the content of destination file is
/// backup to it.
///
/// Where the contents of this file will be copy to.
/// If specified the destFile is backup to it.
///
///
///
///
/// S3FileInfo of the destination file.
public S3FileInfo Replace(S3FileInfo destFile, S3FileInfo backupFile)
{
if (string.Equals(this.BucketName, destFile.BucketName) && string.Equals(this.ObjectKey, destFile.ObjectKey))
{
throw new ArgumentException("Destination file can not be the same as the source file when doing a replace.", "destFile");
}
if (backupFile != null)
{
destFile.CopyTo(backupFile, true);
}
S3FileInfo ret = CopyTo(destFile, true);
Delete();
return ret;
}
///
/// Replaces the content of the destination file on the local file system with the content from this file from S3.
/// If a backup file is specified then the content of the destination file is backup to it.
///
///
///
///
///
///
///
///
public FileInfo ReplaceLocal(string destinationFileName, string destinationBackupFileName)
{
if (!string.IsNullOrEmpty(destinationBackupFileName))
{
File.Replace(destinationFileName, destinationBackupFileName, null);
}
return CopyToLocal(destinationFileName, true);
}
///
/// Implement the ToString method.
///
///
public override string ToString()
{
return FullName;
}
internal IAmazonS3 S3Client
{
get
{
return s3Client;
}
}
internal string BucketName
{
get
{
return bucket;
}
}
internal string ObjectKey
{
get
{
return key;
}
}
private bool SameClient(S3FileInfo otherFile)
{
return s3Client.Equals(otherFile.S3Client);
}
}
}