-
-
Notifications
You must be signed in to change notification settings - Fork 102
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial work on adding AWS S3 Provider
- Loading branch information
1 parent
0e08491
commit 4ce44fb
Showing
6 changed files
with
275 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
57 changes: 57 additions & 0 deletions
57
src/ImageSharp.Web.Providers.AWS/ImageSharp.Web.Providers.AWS.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<AssemblyTitle>SixLabors.ImageSharp.Web.Providers.AWS</AssemblyTitle> | ||
<Authors>Six Labors and contributors</Authors> | ||
<Company>Six Labors</Company> | ||
<Copyright>Copyright (c) Six Labors and contributors.</Copyright> | ||
<Product>SixLabors.ImageSharp.Web.Providers.AWS</Product> | ||
<Description>A provider for resolving images via AWS S3.</Description> | ||
<NeutralLanguage>en</NeutralLanguage> | ||
|
||
<TargetFramework>netstandard2.0</TargetFramework> | ||
<LangVersion>7.3</LangVersion> | ||
|
||
<GenerateDocumentationFile>true</GenerateDocumentationFile> | ||
<AssemblyName>SixLabors.ImageSharp.Web.Providers.AWS</AssemblyName> | ||
<PackageId>SixLabors.ImageSharp.Web.Providers.AWS</PackageId> | ||
<PackageTags>Image Middleware Resize Crop Gif Jpg Jpeg Bitmap Png Azure</PackageTags> | ||
<PackageIconUrl>https://raw.githubusercontent.com/SixLabors/Branding/master/icons/imagesharp/sixlabors.imagesharp.128.png</PackageIconUrl> | ||
<PackageProjectUrl>https://github.com/SixLabors/ImageSharp.Web</PackageProjectUrl> | ||
<PackageLicenseUrl>http://www.apache.org/licenses/LICENSE-2.0</PackageLicenseUrl> | ||
<RepositoryType>git</RepositoryType> | ||
<RepositoryUrl>https://github.com/SixLabors/ImageSharp.Web</RepositoryUrl> | ||
|
||
<DebugType Condition="$(codecov) != ''">full</DebugType> | ||
<DebugType Condition="$(codecov) == ''">portable</DebugType> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="AWSSDK.S3" Version="3.3.104.23" /> | ||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118"> | ||
<PrivateAssets>All</PrivateAssets> | ||
</PackageReference> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<Compile Include="..\Shared\*.cs" Exclude="bin\**;obj\**;**\*.xproj;packages\**" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\ImageSharp.Web\ImageSharp.Web.csproj" /> | ||
</ItemGroup> | ||
|
||
<PropertyGroup> | ||
<CodeAnalysisRuleSet>..\..\shared-infrastructure\SixLabors.ruleset</CodeAnalysisRuleSet> | ||
<RootNamespace>SixLabors.ImageSharp.Web</RootNamespace> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<AdditionalFiles Include="..\..\shared-infrastructure\stylecop.json" /> | ||
</ItemGroup> | ||
|
||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> | ||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> | ||
</PropertyGroup> | ||
|
||
</Project> |
137 changes: 137 additions & 0 deletions
137
src/ImageSharp.Web.Providers.AWS/Providers/AWSS3StorageImageProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
// Copyright (c) Six Labors and contributors. | ||
// Licensed under the Apache License, Version 2.0. | ||
|
||
using System; | ||
using System.IO; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Amazon.Runtime; | ||
using Amazon.S3; | ||
using Amazon.S3.Model; | ||
using Amazon.Util; | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.Extensions.Logging; | ||
using Microsoft.Extensions.Options; | ||
using SixLabors.ImageSharp.Web.Resolvers; | ||
|
||
namespace SixLabors.ImageSharp.Web.Providers | ||
{ | ||
/// <summary> | ||
/// Returns images stored in AWS S3. | ||
/// </summary> | ||
public class AWSS3StorageImageProvider : IImageProvider | ||
{ | ||
/// <summary> | ||
/// Character array to remove from paths. | ||
/// </summary> | ||
private static readonly char[] SlashChars = { '\\', '/' }; | ||
|
||
private readonly IAmazonS3 amazonS3Client; | ||
private readonly ILogger<AWSS3StorageImageProvider> logger; | ||
private readonly AWSS3StorageImageProviderOptions storageOptions; | ||
private Func<HttpContext, bool> match; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="AWSS3StorageImageProvider"/> class. | ||
/// </summary> | ||
/// <param name="amazonS3Client">Amazon S3 client</param> | ||
/// <param name="logger">Microsoft.Extensions.Logging ILogger</param> | ||
/// <param name="storageOptions">The S3 storage options</param> | ||
public AWSS3StorageImageProvider(IAmazonS3 amazonS3Client, ILogger<AWSS3StorageImageProvider> logger, IOptions<AWSS3StorageImageProviderOptions> storageOptions) | ||
{ | ||
Guard.NotNull(storageOptions, nameof(storageOptions)); | ||
|
||
this.amazonS3Client = amazonS3Client; | ||
this.logger = logger; | ||
this.storageOptions = storageOptions.Value; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public Func<HttpContext, bool> Match | ||
{ | ||
get => this.match ?? this.IsMatch; | ||
set => this.match = value; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public bool IsValidRequest(HttpContext context) | ||
{ | ||
var displayUrl = context.Request.Path; | ||
return Path.GetExtension(displayUrl).EndsWith(".jpg") || Path.GetExtension(displayUrl).EndsWith(".png"); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public async Task<IImageResolver> GetAsync(HttpContext context) | ||
{ | ||
PathString displayUrl = context.Request.Path; | ||
this.logger.LogDebug("Getting image for {ImageUri}", displayUrl); | ||
string imageId = Path.GetFileNameWithoutExtension(displayUrl); | ||
this.logger.LogDebug("Image id is {ImageId}", imageId); | ||
|
||
string imagePath = $"{imageId}.png"; | ||
|
||
bool imageExists = await this.KeyExists(this.storageOptions.BucketName, imagePath); | ||
|
||
if (!imageExists) | ||
{ | ||
this.logger.LogDebug("No image found for {ImageId}", imageId); | ||
return null; | ||
} | ||
|
||
this.logger.LogDebug("Found image {ImageId}", imageId); | ||
|
||
return new AWSS3FileSystemResolver(this.amazonS3Client, this.storageOptions.BucketName, imagePath); | ||
} | ||
|
||
private bool IsMatch(HttpContext context) | ||
{ | ||
string path = context.Request.Path.Value.TrimStart(SlashChars); | ||
return path.StartsWith(this.storageOptions.BucketName, StringComparison.OrdinalIgnoreCase); | ||
} | ||
|
||
// ref https://github.com/aws/aws-sdk-net/blob/master/sdk/src/Services/S3/Custom/_bcl/IO/S3FileInfo.cs#L118 | ||
private async Task<bool> KeyExists(string bucketName, string key, CancellationToken cancellationToken = default(CancellationToken)) | ||
{ | ||
this.logger.LogDebug("Checking for the existence of key {Key} in bucket {BucketName}", key, bucketName); | ||
|
||
try | ||
{ | ||
var request = new GetObjectMetadataRequest | ||
{ | ||
BucketName = bucketName, | ||
Key = key | ||
}; | ||
|
||
((Amazon.Runtime.Internal.IAmazonWebServiceRequest)request) | ||
.AddBeforeRequestHandler(FileIORequestEventHandler); | ||
|
||
// If the object doesn't exist then a "NotFound" will be thrown | ||
await this.amazonS3Client.GetObjectMetadataAsync(request, cancellationToken).ConfigureAwait(false); | ||
return true; | ||
} | ||
catch (AmazonS3Exception e) | ||
{ | ||
if (string.Equals(e.ErrorCode, "NoSuchBucket")) | ||
{ | ||
return false; | ||
} | ||
|
||
if (string.Equals(e.ErrorCode, "NotFound")) | ||
{ | ||
return false; | ||
} | ||
|
||
throw; | ||
} | ||
} | ||
|
||
private static void FileIORequestEventHandler(object sender, RequestEventArgs args) | ||
{ | ||
if (args is WebServiceRequestEventArgs wsArgs) | ||
{ | ||
string currentUserAgent = wsArgs.Headers[AWSSDKUtils.UserAgentHeader]; | ||
wsArgs.Headers[AWSSDKUtils.UserAgentHeader] = currentUserAgent + " FileIO"; | ||
} | ||
} | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
src/ImageSharp.Web.Providers.AWS/Providers/AWSS3StorageImageProviderOptions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// Copyright (c) Six Labors and contributors. | ||
// Licensed under the Apache License, Version 2.0. | ||
|
||
namespace SixLabors.ImageSharp.Web.Providers | ||
{ | ||
/// <summary> | ||
/// Configuration options for the <see cref="AWSS3StorageImageProvider"/> provider. | ||
/// </summary> | ||
public class AWSS3StorageImageProviderOptions | ||
{ | ||
/// <summary> | ||
/// Gets or sets the bucket name. | ||
/// Must conform to AWS S3 bucket naming guidelines. | ||
/// </summary> | ||
public string BucketName { get; set; } | ||
} | ||
} |
47 changes: 47 additions & 0 deletions
47
src/ImageSharp.Web.Providers.AWS/Resolvers/AWSS3FileSystemResolver.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// Copyright (c) Six Labors and contributors. | ||
// Licensed under the Apache License, Version 2.0. | ||
|
||
using System.IO; | ||
using System.Threading.Tasks; | ||
using Amazon.S3; | ||
using Amazon.S3.Model; | ||
|
||
namespace SixLabors.ImageSharp.Web.Resolvers | ||
{ | ||
/// <summary> | ||
/// Provides means to manage image buffers within the AWS S3 file system. | ||
/// </summary> | ||
public class AWSS3FileSystemResolver : IImageResolver | ||
{ | ||
private readonly IAmazonS3 amazonS3; | ||
private readonly string bucketName; | ||
private readonly string imagePath; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="AWSS3FileSystemResolver"/> class. | ||
/// </summary> | ||
/// <param name="amazonS3">Amazon S3 Client</param> | ||
/// <param name="bucketName">Bucket Name for where the files are</param> | ||
/// <param name="imagePath">S3 Key</param> | ||
public AWSS3FileSystemResolver(IAmazonS3 amazonS3, string bucketName, string imagePath) | ||
{ | ||
this.amazonS3 = amazonS3; | ||
this.bucketName = bucketName; | ||
this.imagePath = imagePath; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public async Task<ImageMetadata> GetMetaDataAsync() | ||
{ | ||
GetObjectMetadataResponse metadata = await this.amazonS3.GetObjectMetadataAsync(this.bucketName, this.imagePath); | ||
return new ImageMetadata(metadata.LastModified); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public async Task<Stream> OpenReadAsync() | ||
{ | ||
GetObjectResponse s3Object = await this.amazonS3.GetObjectAsync(this.bucketName, this.imagePath); | ||
return s3Object.ResponseStream; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters