PageRenderTime 41ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/Providers/S3StorageProvider.cs

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