Add custom PrependBasePathFileProvider to handle media files with special characters (#12936)

(cherry picked from commit e9883ea063)
This commit is contained in:
Kenn Jacobsen
2022-09-13 09:11:46 +02:00
committed by Sebastiaan Janssen
parent 22576edd70
commit 07b7ef200a
2 changed files with 96 additions and 2 deletions

View File

@@ -0,0 +1,94 @@
using Dazinator.Extensions.FileProviders;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Primitives;
namespace Umbraco.Cms.Web.Common.Media;
/// <summary>
/// Prepends a base path to files / directories from an underlying file provider.
/// </summary>
/// <remarks>
/// This is a clone-and-own of PrependBasePathFileProvider from the Dazinator project, cleaned up and tweaked to work
/// for serving media files with special characters.
/// Reference issue: https://github.com/umbraco/Umbraco-CMS/issues/12903
/// A PR has been submitted to the Dazinator project: https://github.com/dazinator/Dazinator.Extensions.FileProviders/pull/53
/// If that PR is accepted, the Dazinator dependency should be updated and this class should be removed.
/// </remarks>
internal class MediaPrependBasePathFileProvider : IFileProvider
{
private readonly PathString _basePath;
private readonly IFileProvider _underlyingFileProvider;
private readonly IFileInfo _baseDirectoryFileInfo;
private static readonly char[] _splitChar = { '/' };
public MediaPrependBasePathFileProvider(string? basePath, IFileProvider underlyingFileProvider)
{
_basePath = new PathString(basePath);
_baseDirectoryFileInfo = new DirectoryFileInfo(_basePath.ToString().TrimStart(_splitChar));
_underlyingFileProvider = underlyingFileProvider;
}
protected virtual bool TryMapSubPath(string originalSubPath, out PathString newSubPath)
{
if (!string.IsNullOrEmpty(originalSubPath))
{
PathString originalPathString;
originalPathString = originalSubPath[0] != '/' ? new PathString('/' + originalSubPath) : new PathString(originalSubPath);
if (originalPathString.HasValue && originalPathString.StartsWithSegments(_basePath, out PathString remaining))
{
// var childPath = originalPathString.Remove(0, _basePath.Value.Length);
newSubPath = remaining;
return true;
}
}
newSubPath = null;
return false;
}
public IDirectoryContents GetDirectoryContents(string subpath)
{
if (string.IsNullOrEmpty(subpath))
{
// return root / base directory.
return new EnumerableDirectoryContents(_baseDirectoryFileInfo);
}
if (TryMapSubPath(subpath, out PathString newPath))
{
IDirectoryContents? contents = _underlyingFileProvider.GetDirectoryContents(newPath);
return contents;
}
return new NotFoundDirectoryContents();
}
public IFileInfo GetFileInfo(string subpath)
{
if (TryMapSubPath(subpath, out PathString newPath))
{
// KJA changed: use explicit newPath.Value instead of implicit newPath string operator (which calls ToString())
IFileInfo? result = _underlyingFileProvider.GetFileInfo(newPath.Value);
return result;
}
return new NotFoundFileInfo(subpath);
}
public IChangeToken Watch(string filter)
{
// We check if the pattern starts with the base path, and remove it if necessary.
// otherwise we just pass the pattern through unaltered.
if (TryMapSubPath(filter, out PathString newPath))
{
// KJA changed: use explicit newPath.Value instead of implicit newPath string operator (which calls ToString())
IChangeToken? result = _underlyingFileProvider.Watch(newPath.Value);
return result;
}
return _underlyingFileProvider.Watch(newPath);
}
}