11// Copyright (c) Six Labors.
22// Licensed under the Apache License, Version 2.0.
33
4+ using System ;
45using System . Collections . Generic ;
6+ using System . Globalization ;
57using System . IO ;
8+ using System . Threading ;
69using System . Threading . Tasks ;
710using Amazon ;
811using Amazon . S3 ;
@@ -18,6 +21,12 @@ namespace SixLabors.ImageSharp.Web.Caching.AWS
1821 /// </summary>
1922 public class AWSS3StorageCache : IImageCache
2023 {
24+ private static readonly TaskFactory TaskFactory = new
25+ ( CancellationToken . None ,
26+ TaskCreationOptions . None ,
27+ TaskContinuationOptions . None ,
28+ TaskScheduler . Default ) ;
29+
2130 private readonly IAmazonS3 amazonS3Client ;
2231 private readonly string bucket ;
2332
@@ -30,38 +39,18 @@ public AWSS3StorageCache(IOptions<AWSS3StorageCacheOptions> cacheOptions)
3039 Guard . NotNull ( cacheOptions , nameof ( cacheOptions ) ) ;
3140 AWSS3StorageCacheOptions options = cacheOptions . Value ;
3241 this . bucket = options . BucketName ;
33-
34- if ( ! string . IsNullOrEmpty ( options . Endpoint ) &&
35- options . AccessKey != null &&
36- options . AccessSecret != null )
37- {
38- var config = new AmazonS3Config { ServiceURL = options . Endpoint , ForcePathStyle = true } ;
39- this . amazonS3Client = new AmazonS3Client ( options . AccessKey , options . AccessSecret , config ) ;
40- }
41- else if ( ! string . IsNullOrEmpty ( options . AccessKey ) &&
42- ! string . IsNullOrEmpty ( options . AccessSecret ) &&
43- ! string . IsNullOrEmpty ( options . Region ) )
44- {
45- var region = RegionEndpoint . GetBySystemName ( options . Region ) ;
46- this . amazonS3Client = new AmazonS3Client ( options . AccessKey , options . AccessSecret , region ) ;
47- }
48- else
49- {
50- var region = RegionEndpoint . GetBySystemName ( options . Region ) ;
51- this . amazonS3Client = new AmazonS3Client ( region ) ;
52- }
42+ this . amazonS3Client = CreateClient ( options ) ;
5343 }
5444
5545 /// <inheritdoc/>
5646 public async Task < IImageCacheResolver > GetAsync ( string key )
5747 {
58- var request = new GetObjectMetadataRequest ( ) { BucketName = this . bucket , Key = key } ;
59-
48+ GetObjectMetadataRequest request = new ( ) { BucketName = this . bucket , Key = key } ;
6049 try
6150 {
62- await this . amazonS3Client . GetObjectMetadataAsync ( request ) ;
63-
64- return new AWSS3StorageCacheResolver ( this . amazonS3Client , this . bucket , key ) ;
51+ // HEAD request throws a 404 if not found.
52+ MetadataCollection metadata = ( await this . amazonS3Client . GetObjectMetadataAsync ( request ) ) . Metadata ;
53+ return new AWSS3StorageCacheResolver ( this . amazonS3Client , this . bucket , key , metadata ) ;
6554 }
6655 catch
6756 {
@@ -82,7 +71,6 @@ public Task SetAsync(string key, Stream stream, ImageCacheMetadata metadata)
8271 } ;
8372
8473 var dt = metadata . ToDictionary ( ) ;
85-
8674 foreach ( KeyValuePair < string , string > d in dt )
8775 {
8876 request . Metadata . Add ( d . Key , d . Value ) ;
@@ -92,46 +80,28 @@ public Task SetAsync(string key, Stream stream, ImageCacheMetadata metadata)
9280 }
9381
9482 /// <summary>
95- /// Creates a new container under the specified account if a container
83+ /// Creates a new bucket under the specified account if a bucket
9684 /// with the same name does not already exist.
9785 /// </summary>
98- /// <param name="options">The Azure Blob Storage cache options.</param>
86+ /// <param name="options">The AWS S3 Storage cache options.</param>
9987 /// <param name="acl">
10088 /// Specifies whether data in the bucket may be accessed publicly and the level of access.
10189 /// <see cref="S3CannedACL.PublicRead"/> specifies full public read access for bucket
102- /// and object data. <see cref="S3CannedACL.Private"/> specifies that the container
90+ /// and object data. <see cref="S3CannedACL.Private"/> specifies that the bucket
10391 /// data is private to the account owner.
10492 /// </param>
10593 /// <returns>
106- /// If the container does not already exist, a <see cref="PutBucketResponse"/> describing the newly
107- /// created container . If the container already exists, <see langword="null"/>.
94+ /// If the bucket does not already exist, a <see cref="PutBucketResponse"/> describing the newly
95+ /// created bucket . If the container already exists, <see langword="null"/>.
10896 /// </returns>
10997 public static PutBucketResponse CreateIfNotExists (
11098 AWSS3StorageCacheOptions options ,
11199 S3CannedACL acl )
112100 {
113- AmazonS3Client amazonS3Client ;
114- bool foundBucket = false ;
115-
116- if ( ! string . IsNullOrEmpty ( options . Endpoint ) &&
117- options . AccessKey != null &&
118- options . AccessSecret != null )
119- {
120- var config = new AmazonS3Config { ServiceURL = options . Endpoint , ForcePathStyle = true } ;
121- amazonS3Client = new AmazonS3Client ( options . AccessKey , options . AccessSecret , config ) ;
122- }
123- else if ( ! string . IsNullOrEmpty ( options . AccessKey ) &&
124- ! string . IsNullOrEmpty ( options . AccessSecret ) )
125- {
126- amazonS3Client = new AmazonS3Client ( options . AccessKey , options . AccessSecret ) ;
127- }
128- else
129- {
130- amazonS3Client = new AmazonS3Client ( RegionEndpoint . GetBySystemName ( options . Region ) ) ;
131- }
132-
133- ListBucketsResponse listBucketsResponse = amazonS3Client . ListBucketsAsync ( ) . GetAwaiter ( ) . GetResult ( ) ;
101+ AmazonS3Client client = CreateClient ( options ) ;
134102
103+ bool foundBucket = false ;
104+ ListBucketsResponse listBucketsResponse = RunSync ( ( ) => client . ListBucketsAsync ( ) ) ;
135105 foreach ( S3Bucket b in listBucketsResponse . Buckets )
136106 {
137107 if ( b . BucketName == options . BucketName )
@@ -150,13 +120,57 @@ public static PutBucketResponse CreateIfNotExists(
150120 CannedACL = acl
151121 } ;
152122
153- PutBucketResponse putBucketResponse =
154- amazonS3Client . PutBucketAsync ( putBucketRequest ) . GetAwaiter ( ) . GetResult ( ) ;
155-
156- return putBucketResponse ;
123+ return RunSync ( ( ) => client . PutBucketAsync ( putBucketRequest ) ) ;
157124 }
158125
159126 return null ;
160127 }
128+
129+ private static AmazonS3Client CreateClient ( AWSS3StorageCacheOptions options )
130+ {
131+ if ( ! string . IsNullOrWhiteSpace ( options . Endpoint ) )
132+ {
133+ // AccessKey can be empty.
134+ // AccessSecret can be empty.
135+ AmazonS3Config config = new ( ) { ServiceURL = options . Endpoint , ForcePathStyle = true } ;
136+ return new AmazonS3Client ( options . AccessKey , options . AccessSecret , config ) ;
137+ }
138+ else if ( ! string . IsNullOrWhiteSpace ( options . AccessKey ) )
139+ {
140+ // AccessSecret can be empty.
141+ Guard . NotNullOrWhiteSpace ( options . Region , nameof ( options . Region ) ) ;
142+ var region = RegionEndpoint . GetBySystemName ( options . Region ) ;
143+ return new AmazonS3Client ( options . AccessKey , options . AccessSecret , region ) ;
144+ }
145+ else if ( ! string . IsNullOrWhiteSpace ( options . Region ) )
146+ {
147+ var region = RegionEndpoint . GetBySystemName ( options . Region ) ;
148+ return new AmazonS3Client ( region ) ;
149+ }
150+ else
151+ {
152+ throw new ArgumentException ( "Invalid configuration." , nameof ( options ) ) ;
153+ }
154+ }
155+
156+ /// <summary>
157+ /// Executes an async <see cref="Task{TResult}"/> method which has
158+ /// a <paramref name="task"/> return type synchronously.
159+ /// <see href="https://github.com/aspnet/AspNetIdentity/blob/b7826741279450c58b230ece98bd04b4815beabf/src/Microsoft.AspNet.Identity.Core/AsyncHelper.cs"/>
160+ /// </summary>
161+ /// <typeparam name="TResult">The type of result to return.</typeparam>
162+ /// <param name="task">The task to excecute.</param>
163+ /// <returns>The <typeparamref name="TResult"/>.</returns>
164+ private static TResult RunSync < TResult > ( Func < Task < TResult > > task )
165+ {
166+ CultureInfo cultureUi = CultureInfo . CurrentUICulture ;
167+ CultureInfo culture = CultureInfo . CurrentCulture ;
168+ return TaskFactory . StartNew ( ( ) =>
169+ {
170+ Thread . CurrentThread . CurrentCulture = culture ;
171+ Thread . CurrentThread . CurrentUICulture = cultureUi ;
172+ return task ( ) ;
173+ } ) . Unwrap ( ) . GetAwaiter ( ) . GetResult ( ) ;
174+ }
161175 }
162176}
0 commit comments