/Providers/S3StorageProvider.cs
C# | 303 lines | 224 code | 38 blank | 41 comment | 10 complexity | eea00c7adf621cd52652e9e60e67c7e7 MD5 | raw file
1using System; 2using System.Collections.Generic; 3using System.Configuration; 4using System.IO; 5using System.Linq; 6using System.Web.Hosting; 7using Amazon.S3.Util; 8using Orchard.Environment.Configuration; 9using Orchard.Localization; 10using Orchard.Validation; 11using Orchard.FileSystems.Media; 12using Amazon.S3.Model; 13using Werul.S3StorageProvider.Models; 14using Orchard.Environment.Extensions; 15using Amazon.S3; 16using Orchard; 17using Orchard.ContentManagement; 18 19namespace Werul.S3StorageProvider.Providers { 20 [OrchardSuppressDependency("Orchard.FileSystems.Media.FileSystemStorageProvider")] 21 public class S3StorageProvider : IStorageProvider { 22 23 private readonly AmazonS3Config _S3Config; 24 private readonly IOrchardServices Services; 25 26 public S3StorageProvider(IOrchardServices services) 27 { 28 Services = services; 29 _S3Config = new AmazonS3Config() 30 { 31 ServiceURL = "s3.amazonaws.com", 32 CommunicationProtocol = Amazon.S3.Model.Protocol.HTTP, 33 }; 34 35 T = NullLocalizer.Instance; 36 } 37 38 public string PublicPath 39 { 40 get 41 { 42 return string.Format("https://{0}.s3.amazonaws.com/", BucketName); 43 } 44 } 45 public string AWSAccessKey 46 { 47 get 48 { 49 return Services.WorkContext.CurrentSite.As<S3StorageProviderSettingsPart>().Record.AWSAccessKey; 50 } 51 } 52 public string AWSSecretKey 53 { 54 get 55 { 56 return Services.WorkContext.CurrentSite.As<S3StorageProviderSettingsPart>().Record.AWSSecretKey; 57 } 58 } 59 public string BucketName 60 { 61 get 62 { 63 return Services.WorkContext.CurrentSite.As<S3StorageProviderSettingsPart>().Record.BucketName; 64 } 65 } 66 67 public Localizer T { get; set; } 68 69 /// <summary> 70 /// Retrieves the public URL for a given file within the storage provider. 71 /// </summary> 72 /// <param name="path">The relative path within the storage provider.</param> 73 /// <returns>The public URL.</returns> 74 public string GetPublicUrl(string path) 75 { 76 return string.IsNullOrEmpty(path) ? PublicPath : Path.Combine(PublicPath, path).Replace(Path.DirectorySeparatorChar, '/'); 77 } 78 79 /// <summary> 80 /// Retrieves a file within the storage provider. 81 /// </summary> 82 /// <param name="path">The relative path to the file within the storage provider.</param> 83 /// <returns>The file.</returns> 84 /// <exception cref="ArgumentException">If the file is not found.</exception> 85 public IStorageFile GetFile(string path) 86 { 87 //not sure if we should use a temp directory here or what 88 throw new NotImplementedException(); 89 } 90 91 /// <summary> 92 /// Lists the files within a storage provider's path. 93 /// </summary> 94 /// <param name="path">The relative path to the folder which files to list.</param> 95 /// <returns>The list of files in the folder.</returns> 96 public IEnumerable<IStorageFile> ListFiles(string path) 97 { 98 var files = new List<S3StorageFile>(); 99 using (var client = Amazon.AWSClientFactory.CreateAmazonS3Client(AWSAccessKey, AWSSecretKey, _S3Config)) { 100 var request = new ListObjectsRequest(); 101 request.BucketName = BucketName; 102 request.Prefix = path; 103 104 using (ListObjectsResponse response = client.ListObjects(request)) { 105 foreach (var entry in response.S3Objects.Where(e => e.Key.Last() != '/' && e.Key.Count(c => c == '/') == path.Count(c => c == '/'))) { 106 var mimeType = AmazonS3Util.MimeTypeFromExtension(entry.Key.Substring(entry.Key.LastIndexOf("."))); 107 files.Add(new S3StorageFile(entry, mimeType)); 108 } 109 } 110 } 111 112 return files; 113 } 114 115 /// <summary> 116 /// Lists the folders within a storage provider's path. 117 /// </summary> 118 /// <param name="path">The relative path to the folder which folders to list.</param> 119 /// <returns>The list of folders in the folder.</returns> 120 public IEnumerable<IStorageFolder> ListFolders(string path) 121 { 122 var folders = new List<S3StorageFolder>(); 123 124 using (var client = Amazon.AWSClientFactory.CreateAmazonS3Client(AWSAccessKey, AWSSecretKey, _S3Config)) { 125 path = path ?? ""; 126 var request = new ListObjectsRequest(); 127 request.BucketName = BucketName; 128 request.Prefix = path; 129 130 using (ListObjectsResponse response = client.ListObjects(request)) { 131 foreach (var entry in response.S3Objects.Where(e => e.Key.Last() == '/' && e.Key.Count(c => c == '/') == path.Count(c => c == '/') + 1)) { 132 var folderSize = ListFiles(entry.Key).Sum(x => x.GetSize()); 133 folders.Add(new S3StorageFolder(entry, folderSize)); 134 } 135 } 136 } 137 138 return folders; 139 } 140 141 /// <summary> 142 /// Tries to create a folder in the storage provider. 143 /// </summary> 144 /// <param name="path">The relative path to the folder to be created.</param> 145 /// <returns>True if success; False otherwise.</returns> 146 public bool TryCreateFolder(string path) 147 { 148 try 149 { 150 using (var client = Amazon.AWSClientFactory.CreateAmazonS3Client(AWSAccessKey, AWSSecretKey, _S3Config)) 151 { 152 var key = string.Format(@"{0}/", path); 153 var request = new PutObjectRequest().WithBucketName(BucketName).WithKey(key); 154 request.InputStream = new MemoryStream(); 155 client.PutObject(request); 156 } 157 } 158 catch 159 { 160 return false; 161 } 162 163 return true; 164 } 165 166 /// <summary> 167 /// Creates a folder in the storage provider. 168 /// </summary> 169 /// <param name="path">The relative path to the folder to be created.</param> 170 /// <exception cref="ArgumentException">If the folder already exists.</exception> 171 public void CreateFolder(string path) 172 { 173 try 174 { 175 using (var client = Amazon.AWSClientFactory.CreateAmazonS3Client(AWSAccessKey, AWSSecretKey, _S3Config)) 176 { 177 var key = string.Format(@"{0}/", path); 178 var request = new PutObjectRequest().WithBucketName(BucketName).WithKey(key); 179 request.InputStream = new MemoryStream(); 180 client.PutObject(request); 181 } 182 } 183 catch (Exception ex) 184 { 185 throw new ArgumentException(T("Directory {0} already exists", path).ToString(), ex); 186 } 187 } 188 189 public void DeleteFolder(string path) { 190 using (var client = Amazon.AWSClientFactory.CreateAmazonS3Client(AWSAccessKey, AWSSecretKey, _S3Config)) { 191 DeleteFolder(path, client); 192 } 193 } 194 195 private void DeleteFolder(string path, AmazonS3 client) { 196 //TODO: Refactor to use async deletion? 197 foreach (var folder in ListFolders(path)) { 198 DeleteFolder(folder.GetPath(), client); 199 } 200 201 foreach (var file in ListFiles(path)) { 202 DeleteFile(file.GetPath(), client); 203 } 204 205 var request = new DeleteObjectRequest() { 206 BucketName = BucketName, 207 Key = path 208 }; 209 210 using (DeleteObjectResponse response = client.DeleteObject(request)) { 211 212 } 213 } 214 215 public void RenameFolder(string oldPath, string newPath) 216 { 217 // Todo: recursive on all keys with prefix 218 throw new NotImplementedException("Folder renaming currently not supported"); 219 } 220 221 public void DeleteFile(string path) 222 { 223 using (var client = Amazon.AWSClientFactory.CreateAmazonS3Client(AWSAccessKey, AWSSecretKey, _S3Config)) 224 { 225 DeleteFile(path, client); 226 } 227 } 228 229 private void DeleteFile(string path, AmazonS3 client) 230 { 231 var request = new DeleteObjectRequest() { 232 BucketName = BucketName, 233 Key = path 234 }; 235 236 using (DeleteObjectResponse response = client.DeleteObject(request)) {} 237 } 238 239 public void RenameFile(string oldPath, string newPath) 240 { 241 using (var client = Amazon.AWSClientFactory.CreateAmazonS3Client(AWSAccessKey, AWSSecretKey, _S3Config)) { 242 RenameObject(oldPath, newPath, client); 243 244 //Delete the original 245 DeleteFile(oldPath); 246 } 247 } 248 249 public IStorageFile CreateFile(string path) 250 { 251 throw new NotImplementedException("File creation currently not supported."); 252 } 253 254 private void RenameObject(string oldPath, string newPath, AmazonS3 client) { 255 CopyObjectRequest copyRequest = new CopyObjectRequest() 256 .WithSourceBucket(BucketName) 257 .WithSourceKey(oldPath) 258 .WithDestinationBucket(BucketName) 259 .WithDestinationKey(newPath) 260 .WithCannedACL(S3CannedACL.PublicRead); 261 262 client.CopyObject(copyRequest); 263 } 264 265 /// <summary> 266 /// Tries to save a stream in the storage provider. 267 /// </summary> 268 /// <param name="path">The relative path to the file to be created.</param> 269 /// <param name="inputStream">The stream to be saved.</param> 270 /// <returns>True if success; False otherwise.</returns> 271 public bool TrySaveStream(string path, Stream inputStream) 272 { 273 try 274 { 275 SaveStream(path, inputStream); 276 } 277 catch 278 { 279 return false; 280 } 281 282 return true; 283 } 284 285 public void SaveStream(string path, Stream inputStream) 286 { 287 using (var client = Amazon.AWSClientFactory.CreateAmazonS3Client(AWSAccessKey, AWSSecretKey, _S3Config)) 288 { 289 PutObjectRequest request = new PutObjectRequest(); 290 request.WithBucketName(BucketName).WithKey(path).WithCannedACL(S3CannedACL.PublicRead).WithInputStream(inputStream); 291 using (var response = client.PutObject(request)) 292 { 293 294 } 295 } 296 } 297 298 public string Combine(string path1, string path2) 299 { 300 return path1 + path2; 301 } 302 } 303}