Skip to content

Commit c99f07c

Browse files
Merge pull request #214 from SixLabors/js/physical-provider
Decouple PhysicalFileSystemProvider from WebRootFileProvider
2 parents b15c4d4 + a564ab7 commit c99f07c

10 files changed

Lines changed: 118 additions & 54 deletions

src/Directory.Build.targets

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@
2121
<PackageReference Update="Azure.Storage.Blobs" Version="12.10.0" />
2222
<PackageReference Update="Microsoft.IO.RecyclableMemoryStream" Version="2.2.0" />
2323
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
24-
<PackageReference Update="MinVer" PrivateAssets="All" Version="2.3.0" />
25-
<PackageReference Update="SixLabors.ImageSharp" Version="2.0.0-alpha.0.156" />
24+
<PackageReference Update="SixLabors.ImageSharp" Version="2.0.0-alpha.0.165" />
2625
</ItemGroup>
2726

2827
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">

src/ImageSharp.Web/Caching/LegacyV1CacheKey.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Web.Caching
99
{
1010
/// <summary>
1111
/// Maintained for compatibility purposes only this cache key implementation generates the same
12-
/// out as the V1 middleware. If possible, it is recommended to use the <see cref="UriRelativeCacheKey"/>.
12+
/// out as the V1 middleware. If possible, it is recommended to use the <see cref="UriRelativeLowerInvariantCacheKey"/>.
1313
/// </summary>
1414
public class LegacyV1CacheKey : ICacheKey
1515
{

src/ImageSharp.Web/Caching/PhysicalFileSystemCache.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public PhysicalFileSystemCache(
5858
Guard.NotNullOrWhiteSpace(environment.WebRootPath, nameof(environment.WebRootPath));
5959

6060
// Allow configuration of the cache without having to register everything
61-
PhysicalFileSystemCacheOptions cacheOptions = options != null ? options.Value : new PhysicalFileSystemCacheOptions();
61+
PhysicalFileSystemCacheOptions cacheOptions = options != null ? options.Value : new();
6262
this.cacheRootPath = GetCacheRoot(cacheOptions, environment.WebRootPath, environment.ContentRootPath);
6363
this.cacheFolderDepth = (int)cacheOptions.CacheFolderDepth;
6464

@@ -72,15 +72,15 @@ public PhysicalFileSystemCache(
7272
/// <summary>
7373
/// Determine the cache root path
7474
/// </summary>
75-
/// <param name="cacheOptions">the cache options.</param>
76-
/// <param name="webRootPath">the webRootPath.</param>
77-
/// <param name="contentRootPath">the contentRootPath.</param>
78-
/// <returns>root path.</returns>
75+
/// <param name="cacheOptions">The cache options.</param>
76+
/// <param name="webRootPath">The web root path.</param>
77+
/// <param name="contentRootPath">The content root path.</param>
78+
/// <returns><see cref="string"/> representing the fully qualified cache root path.</returns>
7979
internal static string GetCacheRoot(PhysicalFileSystemCacheOptions cacheOptions, string webRootPath, string contentRootPath)
8080
{
81-
string cacheRoot = string.IsNullOrEmpty(cacheOptions.CacheRoot)
81+
string cacheRoot = string.IsNullOrWhiteSpace(cacheOptions.CacheRootPath)
8282
? webRootPath
83-
: cacheOptions.CacheRoot;
83+
: cacheOptions.CacheRootPath;
8484

8585
return Path.IsPathFullyQualified(cacheRoot)
8686
? Path.Combine(cacheRoot, cacheOptions.CacheFolder)

src/ImageSharp.Web/Caching/PhysicalFileSystemCacheOptions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public class PhysicalFileSystemCacheOptions
1919
public uint CacheFolderDepth { get; set; } = 8;
2020

2121
/// <summary>
22-
/// Gets or sets the optional cache root folder.
22+
/// Gets or sets the optional cache root folder path.
2323
/// <para>
2424
/// This value can be <see langword="null"/>, a fully qualified absolute path,
2525
/// or a path relative to the directory that contains the application
@@ -30,6 +30,6 @@ public class PhysicalFileSystemCacheOptions
3030
/// application content files; commonly 'wwwroot'.
3131
/// </para>
3232
/// </summary>
33-
public string CacheRoot { get; set; }
33+
public string CacheRootPath { get; set; }
3434
}
3535
}

src/ImageSharp.Web/DependencyInjection/ImageSharpBuilderExtensions.cs

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
using Microsoft.Extensions.Configuration;
88
using Microsoft.Extensions.DependencyInjection;
99
using Microsoft.Extensions.DependencyInjection.Extensions;
10-
using SixLabors.ImageSharp.Memory;
1110
using SixLabors.ImageSharp.Web.Caching;
1211
using SixLabors.ImageSharp.Web.Commands;
1312
using SixLabors.ImageSharp.Web.Commands.Converters;
@@ -48,27 +47,6 @@ public static IImageSharpBuilder SetRequestParser(this IImageSharpBuilder builde
4847
return builder;
4948
}
5049

51-
/// <summary>
52-
/// Sets the given <see cref="MemoryAllocator"/> adding it to the service collection.
53-
/// </summary>
54-
/// <param name="builder">The core builder.</param>
55-
/// <param name="implementationFactory">The factory method for returning a <see cref="MemoryAllocator"/>.</param>
56-
/// <returns>The <see cref="IImageSharpBuilder"/>.</returns>
57-
[Obsolete("Use ImageSharp.Configuration.MemoryAllocator. This will be removed in a future version.", true)]
58-
public static IImageSharpBuilder SetMemoryAllocator(this IImageSharpBuilder builder, Func<IServiceProvider, MemoryAllocator> implementationFactory)
59-
=> builder;
60-
61-
/// <summary>
62-
/// Sets the given <see cref="MemoryAllocator"/> adding it to the service collection.
63-
/// </summary>
64-
/// <typeparam name="TMemoryAllocator">The type of class implementing <see cref="MemoryAllocator"/>to add.</typeparam>
65-
/// <param name="builder">The core builder.</param>
66-
/// <returns>The <see cref="IImageSharpBuilder"/>.</returns>
67-
[Obsolete("Use ImageSharp.Configuration.MemoryAllocator. This will be removed in a future version.", true)]
68-
public static IImageSharpBuilder SetMemoryAllocator<TMemoryAllocator>(this IImageSharpBuilder builder)
69-
where TMemoryAllocator : MemoryAllocator
70-
=> builder;
71-
7250
/// <summary>
7351
/// Sets the given <see cref="IImageCache"/> adding it to the service collection.
7452
/// </summary>

src/ImageSharp.Web/Middleware/ImageSharpMiddlewareOptions.cs

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,7 @@ public class ImageSharpMiddlewareOptions
9696
/// </summary>
9797
public Func<ImageCommandContext, Task> OnParseCommandsAsync
9898
{
99-
get
100-
{
101-
return this.onParseCommandsAsync;
102-
}
99+
get => this.onParseCommandsAsync;
103100

104101
set
105102
{
@@ -115,10 +112,7 @@ public Func<ImageCommandContext, Task> OnParseCommandsAsync
115112
/// </summary>
116113
public Func<FormattedImage, Task> OnBeforeSaveAsync
117114
{
118-
get
119-
{
120-
return this.onBeforeSaveAsync;
121-
}
115+
get => this.onBeforeSaveAsync;
122116

123117
set
124118
{
@@ -134,10 +128,7 @@ public Func<FormattedImage, Task> OnBeforeSaveAsync
134128
/// </summary>
135129
public Func<ImageProcessingContext, Task> OnProcessedAsync
136130
{
137-
get
138-
{
139-
return this.onProcessedAsync;
140-
}
131+
get => this.onProcessedAsync;
141132

142133
set
143134
{
@@ -153,10 +144,7 @@ public Func<ImageProcessingContext, Task> OnProcessedAsync
153144
/// </summary>
154145
public Func<HttpContext, Task> OnPrepareResponseAsync
155146
{
156-
get
157-
{
158-
return this.onPrepareResponseAsync;
159-
}
147+
get => this.onPrepareResponseAsync;
160148

161149
set
162150
{

src/ImageSharp.Web/Providers/PhysicalFileSystemProvider.cs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22
// Licensed under the Apache License, Version 2.0.
33

44
using System;
5+
using System.IO;
56
using System.Threading.Tasks;
67
using Microsoft.AspNetCore.Hosting;
78
using Microsoft.AspNetCore.Http;
89
using Microsoft.AspNetCore.Http.Extensions;
910
using Microsoft.Extensions.FileProviders;
11+
using Microsoft.Extensions.Options;
1012
using SixLabors.ImageSharp.Web.Resolvers;
1113

1214
namespace SixLabors.ImageSharp.Web.Providers
@@ -29,9 +31,11 @@ public class PhysicalFileSystemProvider : IImageProvider
2931
/// <summary>
3032
/// Initializes a new instance of the <see cref="PhysicalFileSystemProvider"/> class.
3133
/// </summary>
34+
/// <param name="options">The provider configuration options.</param>
3235
/// <param name="environment">The environment used by this middleware.</param>
3336
/// <param name="formatUtilities">Contains various format helper methods based on the current configuration.</param>
3437
public PhysicalFileSystemProvider(
38+
IOptions<PhysicalFileSystemProviderOptions> options,
3539
#if NETCOREAPP2_1
3640
IHostingEnvironment environment,
3741
#else
@@ -40,9 +44,18 @@ public PhysicalFileSystemProvider(
4044
FormatUtilities formatUtilities)
4145
{
4246
Guard.NotNull(environment, nameof(environment));
43-
Guard.NotNull(environment.WebRootFileProvider, nameof(environment.WebRootFileProvider));
4447

45-
this.fileProvider = environment.WebRootFileProvider;
48+
// ContentRootPath is never null.
49+
// https://github.com/dotnet/aspnetcore/blob/b89eba6c3cda331ee98063e3c4a04267ec540315/src/Hosting/Hosting/src/WebHostBuilder.cs#L262
50+
Guard.NotNullOrWhiteSpace(environment.WebRootPath, nameof(environment.WebRootPath));
51+
52+
// Allow configuration of the provider without having to register everything
53+
PhysicalFileSystemProviderOptions providerOptions = options != null ? options.Value : new();
54+
string providerRootPath = GetProviderRoot(providerOptions, environment.WebRootPath, environment.ContentRootPath);
55+
56+
// Ensure provider directory is created before initializing the file provider
57+
Directory.CreateDirectory(providerRootPath);
58+
this.fileProvider = new PhysicalFileProvider(providerRootPath);
4659
this.formatUtilities = formatUtilities;
4760
}
4861

@@ -71,5 +84,23 @@ public Task<IImageResolver> GetAsync(HttpContext context)
7184
var metadata = new ImageMetadata(fileInfo.LastModified.UtcDateTime, fileInfo.Length);
7285
return Task.FromResult<IImageResolver>(new PhysicalFileSystemResolver(fileInfo, metadata));
7386
}
87+
88+
/// <summary>
89+
/// Determine the provider root path
90+
/// </summary>
91+
/// <param name="providerOptions">The provider options.</param>
92+
/// <param name="webRootPath">The web root path.</param>
93+
/// <param name="contentRootPath">The content root path.</param>
94+
/// <returns><see cref="string"/> representing the fully qualified provider root path.</returns>
95+
internal static string GetProviderRoot(PhysicalFileSystemProviderOptions providerOptions, string webRootPath, string contentRootPath)
96+
{
97+
string providerRoot = string.IsNullOrWhiteSpace(providerOptions.ProviderRootPath)
98+
? webRootPath
99+
: providerOptions.ProviderRootPath;
100+
101+
return Path.IsPathFullyQualified(providerRoot)
102+
? providerRoot
103+
: Path.GetFullPath(providerRoot, contentRootPath);
104+
}
74105
}
75106
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
namespace SixLabors.ImageSharp.Web.Providers
5+
{
6+
/// <summary>
7+
/// Configuration options for the <see cref="PhysicalFileSystemProvider" />.
8+
/// </summary>
9+
public class PhysicalFileSystemProviderOptions
10+
{
11+
/// <summary>
12+
/// Gets or sets the optional provider root folder path.
13+
/// <para>
14+
/// This value can be <see langword="null"/>, a fully qualified absolute path,
15+
/// or a path relative to the directory that contains the application
16+
/// content files.
17+
/// </para>
18+
/// <para>
19+
/// If not set, this will default to the directory that contains the web-servable
20+
/// application content files; commonly 'wwwroot'.
21+
/// </para>
22+
/// </summary>
23+
public string ProviderRootPath { get; set; }
24+
}
25+
}

tests/ImageSharp.Web.Tests/Caching/PhysicialFileSystemCacheTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ public void CacheRootFromOptions(string cacheFolder, string cacheRoot, string we
4040
var cacheOptions = new PhysicalFileSystemCacheOptions
4141
{
4242
CacheFolder = cacheFolder,
43-
CacheRoot = cacheRoot
43+
CacheRootPath = cacheRoot
4444
};
4545

46-
var cacheRootResult = PhysicalFileSystemCache.GetCacheRoot(cacheOptions, webRootPath, contentRootPath);
46+
string cacheRootResult = PhysicalFileSystemCache.GetCacheRoot(cacheOptions, webRootPath, contentRootPath);
4747

4848
Assert.Equal(expected, cacheRootResult);
4949
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using SixLabors.ImageSharp.Web.Providers;
5+
using Xunit;
6+
7+
namespace SixLabors.ImageSharp.Web.Tests.Providers
8+
{
9+
public class PhysicalFileSystemProviderTests
10+
{
11+
[Theory]
12+
#if OS_LINUX
13+
[InlineData(null, "wwwroot", "/Users/root/", "/Users/root/wwwroot")]
14+
[InlineData(null, "/Users/WebRoot", "/Users/root/", "/Users/WebRoot")]
15+
[InlineData("providerFolder", "../Temp", "/Users/this/a/root", "/Users/this/a/root/providerFolder")]
16+
[InlineData("../Temp", null, "/Users/this/a/root", "/Users/this/a/Temp")]
17+
[InlineData("/Users/WebRoot", null, "/Users/this/a/root", "/Users/WebRoot")]
18+
#elif OS_OSX
19+
[InlineData(null, "wwwroot", "/Users/root/", "/Users/root/wwwroot")]
20+
[InlineData(null, "/Users/WebRoot", "/Users/root/", "/Users/WebRoot")]
21+
[InlineData("providerFolder", "../Temp", "/Users/this/a/root", "/Users/this/a/root/providerFolder")]
22+
[InlineData("../Temp", null, "/Users/this/a/root", "/Users/this/a/Temp")]
23+
[InlineData("/Users/WebRoot", null, "/Users/this/a/root", "/Users/WebRoot")]
24+
#elif OS_WINDOWS
25+
[InlineData(null, "wwwroot", "C:/root\\", "C:\\root\\wwwroot")]
26+
[InlineData(null, "C:/WebRoot", "C:/root\\", "C:/WebRoot")]
27+
[InlineData("providerFolder", "../Temp", "C:/this/a/root", "C:\\this\\a\\root\\providerFolder")]
28+
[InlineData("../Temp", null, "C:/this/a/root", "C:\\this\\a\\Temp")]
29+
[InlineData("C:/WebRoot", null, "C:/this/a/root", "C:/WebRoot")]
30+
#endif
31+
public void ProvidersRootFromOptions(string providerRoot, string webRootPath, string contentRootPath, string expected)
32+
{
33+
var providerOptions = new PhysicalFileSystemProviderOptions
34+
{
35+
ProviderRootPath = providerRoot,
36+
};
37+
38+
string providerRootResult = PhysicalFileSystemProvider.GetProviderRoot(providerOptions, webRootPath, contentRootPath);
39+
40+
Assert.Equal(expected, providerRootResult);
41+
}
42+
}
43+
}

0 commit comments

Comments
 (0)