Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cache busting parameter to media thumbnails and links in Media Library (Lombiq Technologies: OCORE-143) #15276

Merged
merged 10 commits into from
Mar 1, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,14 @@ function initializeMediaApplication(displayMediaApplication, mediaApplicationUrl
}
});

bus.$on('mediaRenamed', function (newName, newPath, oldPath) {
bus.$on('mediaRenamed', function (newName, newPath, oldPath, newUrl) {
var media = self.mediaItems.filter(function (item) {
return item.mediaPath === oldPath;
})[0];

media.mediaPath = newPath;
media.name = newName;
media.url = newUrl;
});

bus.$on('createFolderRequested', function (media) {
Expand Down Expand Up @@ -501,7 +502,7 @@ function initializeMediaApplication(displayMediaApplication, mediaApplicationUrl
success: function (data) {
var modal = bootstrap.Modal.getOrCreateInstance($('#renameMediaModal'));
modal.hide();
bus.$emit('mediaRenamed', newName, newPath, oldPath);
bus.$emit('mediaRenamed', newName, newPath, oldPath, data.newUrl);
},
error: function (error) {
$('#renameMediaModal-errors').empty();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
Expand All @@ -30,6 +32,8 @@ public class AdminController : Controller
private readonly MediaOptions _mediaOptions;
private readonly IUserAssetFolderNameProvider _userAssetFolderNameProvider;
private readonly IChunkFileUploadService _chunkFileUploadService;
private readonly IFileVersionProvider _fileVersionProvider;
private readonly IServiceProvider _serviceProvider;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private readonly IServiceProvider _serviceProvider;
private readonly IMediaFileStoreCache _mediaFileStoreCache;


public AdminController(
IMediaFileStore mediaFileStore,
Expand All @@ -40,8 +44,9 @@ public AdminController(
ILogger<AdminController> logger,
IStringLocalizer<AdminController> stringLocalizer,
IUserAssetFolderNameProvider userAssetFolderNameProvider,
IChunkFileUploadService chunkFileUploadService
)
IChunkFileUploadService chunkFileUploadService,
IFileVersionProvider fileVersionProvider,
IServiceProvider serviceProvider)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
IServiceProvider serviceProvider)
IMediaFileStoreCache mediaFileStoreCache)

{
_mediaFileStore = mediaFileStore;
_mediaNameNormalizerService = mediaNameNormalizerService;
Expand All @@ -52,6 +57,8 @@ IChunkFileUploadService chunkFileUploadService
S = stringLocalizer;
_userAssetFolderNameProvider = userAssetFolderNameProvider;
_chunkFileUploadService = chunkFileUploadService;
_fileVersionProvider = fileVersionProvider;
_serviceProvider = serviceProvider;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
_serviceProvider = serviceProvider;
_mediaFileStoreCache = mediaFileStoreCache;

}

public async Task<IActionResult> Index()
Expand Down Expand Up @@ -115,7 +122,10 @@ public async Task<ActionResult<IEnumerable<object>>> GetMediaItems(string path,
var allowedExtensions = GetRequestedExtensions(extensions, false);

var allowed = _mediaFileStore.GetDirectoryContentAsync(path)
.WhereAwait(async e => !e.IsDirectory && (allowedExtensions.Count == 0 || allowedExtensions.Contains(Path.GetExtension(e.Path))) && await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaFolder, (object)e.Path))
.WhereAwait(async e =>
!e.IsDirectory &&
(allowedExtensions.Count == 0 || allowedExtensions.Contains(Path.GetExtension(e.Path))) &&
await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaFolder, (object)e.Path))
.Select(e => CreateFileResult(e));

return Ok(await allowed.ToListAsync());
Expand Down Expand Up @@ -202,6 +212,9 @@ public async Task<IActionResult> Upload(string path, string extensions)

var mediaFile = await _mediaFileStore.GetFileInfoAsync(mediaFilePath);

stream.Position = 0;
await PreCacheRemoteMedia(mediaFile, stream);

result.Add(CreateFileResult(mediaFile));
}
catch (Exception ex)
Expand Down Expand Up @@ -309,7 +322,10 @@ public async Task<IActionResult> MoveMedia(string oldPath, string newPath)

await _mediaFileStore.MoveFileAsync(oldPath, newPath);

return Ok();
var newFileInfo = await _mediaFileStore.GetFileInfoAsync(newPath);
await PreCacheRemoteMedia(newFileInfo);

return Ok(new { newUrl = GetCacheBustingMediaPublicUrl(newPath) });
}

[HttpPost]
Expand Down Expand Up @@ -442,7 +458,7 @@ public object CreateFileResult(IFileStoreEntry mediaFile)
size = mediaFile.Length,
lastModify = mediaFile.LastModifiedUtc.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds,
folder = mediaFile.DirectoryPath,
url = _mediaFileStore.MapPathToPublicUrl(mediaFile.Path),
url = GetCacheBustingMediaPublicUrl(mediaFile.Path),
mediaPath = mediaFile.Path,
mime = contentType ?? "application/octet-stream",
mediaText = string.Empty,
Expand Down Expand Up @@ -490,5 +506,38 @@ private HashSet<string> GetRequestedExtensions(string exts, bool fallback)

return [];
}

private string GetCacheBustingMediaPublicUrl(string path) =>
_fileVersionProvider.AddFileVersionToPath(HttpContext.Request.PathBase, _mediaFileStore.MapPathToPublicUrl(path));

// If a remote storage is used, then we need to preemptively cache the newly uploaded or renamed file. Without
// this, the Media Library page will try to load the thumbnail without a cache busting parameter, since
// ShellFileVersionProvider won't find it in the local cache.
// This is not required for files moved across folders, because the folder will be reopened anyway.
private async Task PreCacheRemoteMedia(IFileStoreEntry mediaFile, Stream stream = null)
{
var mediaFileStoreCache = _serviceProvider.GetService<IMediaFileStoreCache>();
if (mediaFileStoreCache == null)
Comment on lines +522 to +523
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
var mediaFileStoreCache = _serviceProvider.GetService<IMediaFileStoreCache>();
if (mediaFileStoreCache == null)
if (_mediaFileStoreCache == null)

{
return;
}

Stream localStream = null;

if (stream == null)
{
localStream = await _mediaFileStore.GetFileStreamAsync(mediaFile);
stream = localStream;
}

try
{
await mediaFileStoreCache.SetCacheAsync(stream, mediaFile, HttpContext.RequestAborted);
}
finally
{
localStream?.Dispose();
}
}
}
}

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ public Task<bool> IsCachedAsync(string path)

public async Task SetCacheAsync(Stream stream, IFileStoreEntry fileStoreEntry, CancellationToken cancellationToken)
{
// File store semantics include a leading slash.
var cachePath = Path.Combine(Root, fileStoreEntry.Path[1..]);
// File store semantics may include a leading slash.
var cachePath = Path.Combine(Root, fileStoreEntry.Path.TrimStart('/'));
var directory = Path.GetDirectoryName(cachePath);

if (!Directory.Exists(directory))
Expand Down
Loading