forked from SixLabors/ImageSharp.Web
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAzureBlobStorageImageProvider.cs
More file actions
154 lines (126 loc) · 4.93 KB
/
AzureBlobStorageImageProvider.cs
File metadata and controls
154 lines (126 loc) · 4.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using Azure.Storage.Blobs;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Options;
using SixLabors.ImageSharp.Web.Resolvers;
using SixLabors.ImageSharp.Web.Resolvers.Azure;
namespace SixLabors.ImageSharp.Web.Providers.Azure;
/// <summary>
/// Returns images stored in Azure Blob Storage.
/// </summary>
public class AzureBlobStorageImageProvider : IImageProvider
{
/// <summary>
/// Character array to remove from paths.
/// </summary>
private static readonly char[] SlashChars = { '\\', '/' };
/// <summary>
/// The containers for the blob services.
/// </summary>
private readonly Dictionary<string, BlobContainerClient> containers
= new();
/// <summary>
/// Contains various helper methods based on the current configuration.
/// </summary>
private readonly FormatUtilities formatUtilities;
/// <summary>
/// A match function used by the resolver to identify itself as the correct resolver to use.
/// </summary>
private Func<HttpContext, bool>? match;
/// <summary>
/// Initializes a new instance of the <see cref="AzureBlobStorageImageProvider"/> class.
/// </summary>
/// <param name="storageOptions">The blob storage options.</param>
/// <param name="formatUtilities">Contains various format helper methods based on the current configuration.</param>
/// <param name="serviceProvider">The current service provider</param>
public AzureBlobStorageImageProvider(
IOptions<AzureBlobStorageImageProviderOptions> storageOptions,
FormatUtilities formatUtilities,
IServiceProvider serviceProvider)
{
Guard.NotNull(storageOptions, nameof(storageOptions));
this.formatUtilities = formatUtilities;
foreach (AzureBlobContainerClientOptions container in storageOptions.Value.BlobContainers)
{
BlobContainerClient containerClient = container.BlobContainerClientProvider == null
? new BlobContainerClient(container.ConnectionString, container.ContainerName)
: container.BlobContainerClientProvider(container, serviceProvider);
this.containers.Add(container.ContainerName!, containerClient);
}
}
/// <inheritdoc/>
public ProcessingBehavior ProcessingBehavior { get; } = ProcessingBehavior.All;
/// <inheritdoc/>
public Func<HttpContext, bool> Match
{
get => this.match ?? this.IsMatch;
set => this.match = value;
}
/// <inheritdoc/>
public async Task<IImageResolver?> GetAsync(HttpContext context)
{
// Strip the leading slash and container name from the HTTP request path and treat
// the remaining path string as the blob name.
// Path has already been correctly parsed before here.
string containerName = string.Empty;
BlobContainerClient? container = null;
// We want an exact match here to ensure that container names starting with
// the same prefix are not mixed up.
string? path = context.Request.Path.Value?.TrimStart(SlashChars);
if (path is null)
{
return null;
}
int index = path.IndexOfAny(SlashChars);
string nameToMatch = index != -1 ? path[..index] : path;
foreach (string key in this.containers.Keys)
{
if (nameToMatch.Equals(key, StringComparison.OrdinalIgnoreCase))
{
containerName = key;
container = this.containers[key];
break;
}
}
// Something has gone horribly wrong for this to happen but check anyway.
if (container is null)
{
return null;
}
// Blob name should be the remaining path string.
string blobName = path.Substring(containerName.Length).TrimStart(SlashChars);
if (string.IsNullOrWhiteSpace(blobName))
{
return null;
}
BlobClient blob = container.GetBlobClient(blobName);
if (!await blob.ExistsAsync())
{
return null;
}
return new AzureBlobStorageImageResolver(blob);
}
/// <inheritdoc/>
public bool IsValidRequest(HttpContext context)
=> this.formatUtilities.TryGetExtensionFromUri(context.Request.GetDisplayUrl(), out _);
private bool IsMatch(HttpContext context)
{
// Only match loosly here for performance.
// Path matching conflicts should be dealt with by configuration.
string? path = context.Request.Path.Value?.TrimStart(SlashChars);
if (path is null)
{
return false;
}
foreach (string container in this.containers.Keys)
{
if (path.StartsWith(container, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
}