Skip to content

Instantly share code, notes, and snippets.

@AEdmunds
Last active December 20, 2015 06:48
Show Gist options
  • Save AEdmunds/d3f4634b9da1f4c4bcc0 to your computer and use it in GitHub Desktop.
Save AEdmunds/d3f4634b9da1f4c4bcc0 to your computer and use it in GitHub Desktop.
S3 Storage Provider
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.S3.Util;
using Orchard;
using Orchard.FileSystems.Media;
using Orchard.Localization;
namespace AppHarbor.FileSystems.Media
{
public class S3StorageProvider : IStorageProvider
{
private readonly AmazonS3Config _s3Config;
private readonly IOrchardServices _services;
public S3StorageProvider(IOrchardServices services)
{
_services = services;
_s3Config = new AmazonS3Config()
{
ServiceURL = "s3.amazonaws.com",
CommunicationProtocol = Amazon.S3.Model.Protocol.HTTP,
};
T = NullLocalizer.Instance;
}
public string PublicPath
{
get
{
return ConfigurationManager.AppSettings["AWS_CND"];
}
}
public string AWSAccessKey
{
get
{
return ConfigurationManager.AppSettings["AWS_AccessKey"];
}
}
public string AWSSecretKey
{
get
{
return ConfigurationManager.AppSettings["AWS_SecretKey"];
}
}
public string BucketName
{
get
{
return ConfigurationManager.AppSettings["AWS_Bucket"];
}
}
public Localizer T { get; set; }
private static string ConvertToRelativeUriPath(string path)
{
var newPath = path.Replace(@"\", "/");
if (newPath.StartsWith("/") || newPath.StartsWith("http://") || newPath.StartsWith("https://"))
throw new ArgumentException("Path must be relative");
return newPath;
}
public bool FileExists(string path) {
var files = new List<S3StorageFile>();
using (var client = Amazon.AWSClientFactory.CreateAmazonS3Client(AWSAccessKey, AWSSecretKey, _s3Config))
{
var request = new ListObjectsRequest();
request.BucketName = BucketName;
request.Prefix = path;
using (ListObjectsResponse response = client.ListObjects(request))
{
foreach (var entry in response.S3Objects.Where(e => e.Key.Last() != '/'))
{
var mimeType = AmazonS3Util.MimeTypeFromExtension(entry.Key.Substring(entry.Key.LastIndexOf(".", System.StringComparison.Ordinal)));
files.Add(new S3StorageFile(entry, mimeType));
}
}
}
return files.Any();
}
/// <summary>
/// Retrieves the public URL for a given file within the storage provider.
/// </summary>
/// <param name="path">The relative path within the storage provider.</param>
/// <returns>The public URL.</returns>
public string GetPublicUrl(string path)
{
return string.IsNullOrEmpty(path) ? PublicPath : Path.Combine(PublicPath, path).Replace(Path.DirectorySeparatorChar, '/');
}
public string GetStoragePath(string url) {
throw new NotImplementedException();
}
/// <summary>
/// Retrieves a file within the storage provider.
/// </summary>
/// <param name="path">The relative path to the file within the storage provider.</param>
/// <returns>The file.</returns>
/// <exception cref="ArgumentException">If the file is not found.</exception>
public IStorageFile GetFile(string path)
{
// seperate folder form file
using (var client = Amazon.AWSClientFactory.CreateAmazonS3Client(AWSAccessKey, AWSSecretKey, _s3Config))
{
var request = new GetObjectRequest();
request.BucketName = BucketName;
request.Key = path;
using (GetObjectResponse response = client.GetObject(request))
{
var mimeType = AmazonS3Util.MimeTypeFromExtension(response.Key.Substring(response.Key.LastIndexOf(".", System.StringComparison.Ordinal)));
var entity = new S3Object {
BucketName = response.BucketName,
Key = response.Key,
Size = response.ContentLength
};
var buffer = new byte[response.ContentLength];
var count = response.ResponseStream.Read(buffer, 0, buffer.Length);
var memoryStream = new MemoryStream(buffer, 0, count);
return new S3StroageFileWithStream(entity, mimeType, memoryStream);
}
//using (GetObjectResponse response = client.GetObject(request))
//{
// response.Key.Substring()
// foreach (var entry in response.S3Objects.Where(e => e.Key == path))
// {
// var mimeType = AmazonS3Util.MimeTypeFromExtension(entry.Key.Substring(entry.Key.LastIndexOf(".", System.StringComparison.Ordinal)));
// return new S3StorageFile(entry, mimeType);
// }
//}
}
}
/// <summary>
/// Lists the files within a storage provider's path.
/// </summary>
/// <param name="path">The relative path to the folder which files to list.</param>
/// <returns>The list of files in the folder.</returns>
public IEnumerable<IStorageFile> ListFiles(string path)
{
var files = new List<S3StorageFile>();
using (var client = Amazon.AWSClientFactory.CreateAmazonS3Client(AWSAccessKey, AWSSecretKey, _s3Config))
{
var request = new ListObjectsRequest();
request.BucketName = BucketName;
request.Prefix = path;
using (ListObjectsResponse response = client.ListObjects(request))
{
foreach (var entry in response.S3Objects.Where(e => e.Key.Last() != '/'))
{
var mimeType = AmazonS3Util.MimeTypeFromExtension(entry.Key.Substring(entry.Key.LastIndexOf(".", System.StringComparison.Ordinal)));
files.Add(new S3StorageFile(entry, mimeType));
}
}
}
return files;
}
public bool FolderExists(string path) {
var folders = new List<S3StorageFolder>();
using (var client = Amazon.AWSClientFactory.CreateAmazonS3Client(AWSAccessKey, AWSSecretKey, _s3Config))
{
path = path ?? "";
var request = new ListObjectsRequest()
.WithBucketName(BucketName)
.WithPrefix(string.Format(@"{0}/", path))
.WithDelimiter(@"/");
using (ListObjectsResponse response = client.ListObjects(request))
{
foreach (var subFolder in response.CommonPrefixes)
{
/* list the sub-folders */
var folderSize = ListFiles(subFolder).Sum(x => x.GetSize());
folders.Add(new S3StorageFolder(subFolder, folderSize));
}
}
}
return folders.Any();
}
/// <summary>
/// Lists the folders within a storage provider's path.
/// </summary>
/// <param name="path">The relative path to the folder which folders to list.</param>
/// <returns>The list of folders in the folder.</returns>
public IEnumerable<IStorageFolder> ListFolders(string path)
{
var folders = new List<S3StorageFolder>();
using (var client = Amazon.AWSClientFactory.CreateAmazonS3Client(AWSAccessKey, AWSSecretKey, _s3Config))
{
path = path ?? "";
var request = new ListObjectsRequest()
.WithBucketName(BucketName)
.WithPrefix(string.Format(@"{0}{1}", path, path == "" ? "" : @"/"))
.WithDelimiter(@"/");
using (ListObjectsResponse response = client.ListObjects(request))
{
foreach (var subFolder in response.CommonPrefixes)
{
/* list the sub-folders */
var folderSize = ListFiles(subFolder).Sum(x => x.GetSize());
folders.Add(new S3StorageFolder(subFolder, folderSize));
}
}
}
return folders;
}
/// <summary>
/// Tries to create a folder in the storage provider.
/// </summary>
/// <param name="path">The relative path to the folder to be created.</param>
/// <returns>True if success; False otherwise.</returns>
public bool TryCreateFolder(string path)
{
try
{
using (var client = Amazon.AWSClientFactory.CreateAmazonS3Client(AWSAccessKey, AWSSecretKey, _s3Config))
{
var key = string.Format(@"{0}/", path);
var request = new PutObjectRequest().WithBucketName(BucketName).WithKey(key);
request.InputStream = new MemoryStream();
client.PutObject(request);
}
}
catch
{
return false;
}
return true;
}
/// <summary>
/// Creates a folder in the storage provider.
/// </summary>
/// <param name="path">The relative path to the folder to be created.</param>
/// <exception cref="ArgumentException">If the folder already exists.</exception>
public void CreateFolder(string path)
{
try
{
using (var client = Amazon.AWSClientFactory.CreateAmazonS3Client(AWSAccessKey, AWSSecretKey, _s3Config))
{
var key = string.Format(@"{0}/", path);
var request = new PutObjectRequest().WithBucketName(BucketName).WithKey(key);
request.InputStream = new MemoryStream();
client.PutObject(request);
}
}
catch (Exception ex)
{
throw new ArgumentException(T("Directory {0} already exists", path).ToString(), ex);
}
}
public void DeleteFolder(string path)
{
using (var client = Amazon.AWSClientFactory.CreateAmazonS3Client(AWSAccessKey, AWSSecretKey, _s3Config))
{
DeleteFolder(path, client);
}
}
private void DeleteFolder(string path, AmazonS3 client)
{
//TODO: Refractor to use async deletion?
foreach (var folder in ListFolders(path))
{
DeleteFolder(folder.GetPath(), client);
}
foreach (var file in ListFiles(path))
{
DeleteFile(file.GetPath(), client);
}
var request = new DeleteObjectRequest()
{
BucketName = BucketName,
Key = path
};
using (DeleteObjectResponse response = client.DeleteObject(request))
{
}
}
public void RenameFolder(string oldPath, string newPath)
{
// Todo: recursive on all keys with prefix
throw new NotImplementedException("Folder renaming currently not supported");
}
public void DeleteFile(string path)
{
using (var client = Amazon.AWSClientFactory.CreateAmazonS3Client(AWSAccessKey, AWSSecretKey, _s3Config))
{
DeleteFile(path, client);
}
}
private void DeleteFile(string path, AmazonS3 client)
{
var request = new DeleteObjectRequest()
{
BucketName = BucketName,
Key = path
};
using (DeleteObjectResponse response = client.DeleteObject(request)) { }
}
public void RenameFile(string oldPath, string newPath)
{
using (var client = Amazon.AWSClientFactory.CreateAmazonS3Client(AWSAccessKey, AWSSecretKey, _s3Config))
{
RenameObject(oldPath, newPath, client);
//Delete the original
DeleteFile(oldPath);
}
}
public IStorageFile CreateFile(string path)
{
throw new NotImplementedException("File creation currently not supported.");
}
private void RenameObject(string oldPath, string newPath, AmazonS3 client)
{
CopyObjectRequest copyRequest = new CopyObjectRequest()
.WithSourceBucket(BucketName)
.WithSourceKey(oldPath)
.WithDestinationBucket(BucketName)
.WithDestinationKey(newPath)
.WithCannedACL(S3CannedACL.PublicRead);
client.CopyObject(copyRequest);
}
/// <summary>
/// Tries to save a stream in the storage provider.
/// </summary>
/// <param name="path">The relative path to the file to be created.</param>
/// <param name="inputStream">The stream to be saved.</param>
/// <returns>True if success; False otherwise.</returns>
public bool TrySaveStream(string path, Stream inputStream)
{
try
{
SaveStream(path, inputStream);
}
catch
{
return false;
}
return true;
}
public void SaveStream(string path, Stream inputStream)
{
using (var client = Amazon.AWSClientFactory.CreateAmazonS3Client(AWSAccessKey, AWSSecretKey, _s3Config))
{
PutObjectRequest request = new PutObjectRequest{
BucketName = BucketName,
Key = path,
CannedACL = S3CannedACL.PublicRead,
InputStream = inputStream,
Timeout = -1,
ReadWriteTimeout = 300000 // 5 minutes in milliseconds
};
//request.WithBucketName().WithKey(path).WithCannedACL(S3CannedACL.PublicRead).WithInputStream(inputStream);
// add far distance experiy date
request.AddHeader("expires", DateTime.Now.AddYears(10).ToString("ddd, dd, MMM yyyy hh:mm:ss GMT"));
using (var response = client.PutObject(request))
{
}
}
}
public string Combine(string path1, string path2)
{
if (path1.EndsWith("/") || path2.StartsWith("/"))
return string.Format("{0}{1}", path1, path2);
return string.Format("{0}/{1}", path1, path2);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment