-
-
Save AEdmunds/d3f4634b9da1f4c4bcc0 to your computer and use it in GitHub Desktop.
S3 Storage Provider
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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