PageRenderTime 42ms CodeModel.GetById 25ms app.highlight 12ms RepoModel.GetById 1ms app.codeStats 0ms

/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}