@@ -13,22 +13,29 @@ namespace SixLabors.ImageSharp.Web.Caching.AWS;
1313/// <summary>
1414/// Implements an AWS S3 Storage based cache.
1515/// </summary>
16- public class AWSS3StorageCache : IImageCache
16+ public class AWSS3StorageCache : IImageCache , IDisposable
1717{
18- private readonly AmazonS3Client amazonS3Client ;
18+ private readonly AmazonS3BucketClient amazonS3Client ;
1919 private readonly string bucketName ;
2020 private readonly string cacheFolder ;
21+ private bool isDisposed ;
2122
2223 /// <summary>
2324 /// Initializes a new instance of the <see cref="AWSS3StorageCache"/> class.
2425 /// </summary>
2526 /// <param name="cacheOptions">The cache options.</param>
26- public AWSS3StorageCache ( IOptions < AWSS3StorageCacheOptions > cacheOptions )
27+ /// <param name="serviceProvider">The current service provider.</param>
28+ public AWSS3StorageCache ( IOptions < AWSS3StorageCacheOptions > cacheOptions , IServiceProvider serviceProvider )
2729 {
2830 Guard . NotNull ( cacheOptions , nameof ( cacheOptions ) ) ;
2931 AWSS3StorageCacheOptions options = cacheOptions . Value ;
30- this . bucketName = options . BucketName ;
31- this . amazonS3Client = AmazonS3ClientFactory . CreateClient ( options ) ;
32+
33+ this . amazonS3Client =
34+ options . S3ClientFactory ? . Invoke ( options , serviceProvider )
35+ ?? AmazonS3ClientFactory . CreateClient ( options ) ;
36+
37+ this . bucketName = this . amazonS3Client . BucketName ;
38+
3239 this . cacheFolder = string . IsNullOrEmpty ( options . CacheFolder )
3340 ? string . Empty
3441 : options . CacheFolder . Trim ( ) . Trim ( '/' ) + '/' ;
@@ -42,8 +49,8 @@ public AWSS3StorageCache(IOptions<AWSS3StorageCacheOptions> cacheOptions)
4249 try
4350 {
4451 // HEAD request throws a 404 if not found.
45- MetadataCollection metadata = ( await this . amazonS3Client . GetObjectMetadataAsync ( request ) ) . Metadata ;
46- return new AWSS3StorageCacheResolver ( this . amazonS3Client , this . bucketName , keyWithFolder , metadata ) ;
52+ MetadataCollection metadata = ( await this . amazonS3Client . Client . GetObjectMetadataAsync ( request ) ) . Metadata ;
53+ return new AWSS3StorageCacheResolver ( this . amazonS3Client . Client , this . bucketName , keyWithFolder , metadata ) ;
4754 }
4855 catch
4956 {
@@ -60,15 +67,16 @@ public Task SetAsync(string key, Stream stream, ImageCacheMetadata metadata)
6067 Key = this . GetKeyWithFolder ( key ) ,
6168 ContentType = metadata . ContentType ,
6269 InputStream = stream ,
63- AutoCloseStream = false
70+ AutoCloseStream = false ,
71+ UseChunkEncoding = false
6472 } ;
6573
6674 foreach ( KeyValuePair < string , string > d in metadata . ToDictionary ( ) )
6775 {
6876 request . Metadata . Add ( d . Key , d . Value ) ;
6977 }
7078
71- return this . amazonS3Client . PutObjectAsync ( request ) ;
79+ return this . amazonS3Client . Client . PutObjectAsync ( request ) ;
7280 }
7381
7482 /// <summary>
@@ -86,16 +94,38 @@ public Task SetAsync(string key, Stream stream, ImageCacheMetadata metadata)
8694 /// If the bucket does not already exist, a <see cref="PutBucketResponse"/> describing the newly
8795 /// created bucket. If the container already exists, <see langword="null"/>.
8896 /// </returns>
89- public static PutBucketResponse ? CreateIfNotExists (
90- AWSS3StorageCacheOptions options ,
91- S3CannedACL acl )
97+ public static PutBucketResponse ? CreateIfNotExists ( AWSS3StorageCacheOptions options , S3CannedACL acl )
9298 => AsyncHelper . RunSync ( ( ) => CreateIfNotExistsAsync ( options , acl ) ) ;
9399
94- private static async Task < PutBucketResponse ? > CreateIfNotExistsAsync (
95- AWSS3StorageCacheOptions options ,
96- S3CannedACL acl )
100+ /// <summary>
101+ /// Releases the unmanaged resources used by the <see cref="AWSS3StorageCache"/> and optionally releases the managed resources.
102+ /// </summary>
103+ /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
104+ protected virtual void Dispose ( bool disposing )
105+ {
106+ if ( ! this . isDisposed )
107+ {
108+ if ( disposing )
109+ {
110+ this . amazonS3Client ? . Dispose ( ) ;
111+ }
112+
113+ this . isDisposed = true ;
114+ }
115+ }
116+
117+ /// <inheritdoc/>
118+ public void Dispose ( )
119+ {
120+ // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
121+ this . Dispose ( disposing : true ) ;
122+ GC . SuppressFinalize ( this ) ;
123+ }
124+
125+ private static async Task < PutBucketResponse ? > CreateIfNotExistsAsync ( AWSS3StorageCacheOptions options , S3CannedACL acl )
97126 {
98- AmazonS3Client client = AmazonS3ClientFactory . CreateClient ( options ) ;
127+ using AmazonS3BucketClient bucketClient = AmazonS3ClientFactory . CreateClient ( options ) ;
128+ AmazonS3Client client = bucketClient . Client ;
99129
100130 bool foundBucket = false ;
101131 ListBucketsResponse listBucketsResponse = await client . ListBucketsAsync ( ) ;
@@ -141,7 +171,7 @@ private static readonly TaskFactory TaskFactory
141171 /// <summary>
142172 /// Executes an async <see cref="Task"/> method synchronously.
143173 /// </summary>
144- /// <param name="task">The task to excecute .</param>
174+ /// <param name="task">The task to execute .</param>
145175 public static void RunSync ( Func < Task > task )
146176 {
147177 CultureInfo cultureUi = CultureInfo . CurrentUICulture ;
@@ -159,7 +189,7 @@ public static void RunSync(Func<Task> task)
159189 /// a <paramref name="task"/> return type synchronously.
160190 /// </summary>
161191 /// <typeparam name="TResult">The type of result to return.</typeparam>
162- /// <param name="task">The task to excecute .</param>
192+ /// <param name="task">The task to execute .</param>
163193 /// <returns>The <typeparamref name="TResult"/>.</returns>
164194 public static TResult RunSync < TResult > ( Func < Task < TResult > > task )
165195 {
0 commit comments