Enhances image manipulation with format (webp encoder, defaults to png)
- computes etag with all manipulation parameters
This commit is contained in:
parent
aa5b84a214
commit
3adb7ecb1c
|
@ -685,14 +685,16 @@ namespace Oqtane.Controllers
|
||||||
{
|
{
|
||||||
if (!bool.TryParse(recreate, out _)) recreate = "false";
|
if (!bool.TryParse(recreate, out _)) recreate = "false";
|
||||||
|
|
||||||
string imagepath = filepath.Replace(Path.GetExtension(filepath), "." + width.ToString() + "x" + height.ToString() + ".png");
|
string format = "png";
|
||||||
|
|
||||||
|
string imagepath = filepath.Replace(Path.GetExtension(filepath), "." + width.ToString() + "x" + height.ToString() + "." + format);
|
||||||
if (!System.IO.File.Exists(imagepath) || bool.Parse(recreate))
|
if (!System.IO.File.Exists(imagepath) || bool.Parse(recreate))
|
||||||
{
|
{
|
||||||
// user has edit access to folder or folder supports the image size being created
|
// user has edit access to folder or folder supports the image size being created
|
||||||
if (_userPermissions.IsAuthorized(User, PermissionNames.Edit, file.Folder.PermissionList) ||
|
if (_userPermissions.IsAuthorized(User, PermissionNames.Edit, file.Folder.PermissionList) ||
|
||||||
(!string.IsNullOrEmpty(file.Folder.ImageSizes) && (file.Folder.ImageSizes == "*" || file.Folder.ImageSizes.ToLower().Split(",").Contains(width.ToString() + "x" + height.ToString()))))
|
(!string.IsNullOrEmpty(file.Folder.ImageSizes) && (file.Folder.ImageSizes == "*" || file.Folder.ImageSizes.ToLower().Split(",").Contains(width.ToString() + "x" + height.ToString()))))
|
||||||
{
|
{
|
||||||
imagepath = _imageService.CreateImage(filepath, width, height, mode, position, background, rotate, imagepath);
|
imagepath = _imageService.CreateImage(filepath, width, height, mode, position, background, rotate, format, imagepath);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -102,7 +102,16 @@ namespace Oqtane.Pages
|
||||||
// appends the query string to the redirect url
|
// appends the query string to the redirect url
|
||||||
if (Request.QueryString.HasValue && !string.IsNullOrWhiteSpace(Request.QueryString.Value))
|
if (Request.QueryString.HasValue && !string.IsNullOrWhiteSpace(Request.QueryString.Value))
|
||||||
{
|
{
|
||||||
url += Request.QueryString.Value;
|
if (url.Contains('?'))
|
||||||
|
{
|
||||||
|
url += "&";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
url += "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
url += Request.QueryString.Value.Substring(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return RedirectPermanent(url);
|
return RedirectPermanent(url);
|
||||||
|
@ -122,25 +131,49 @@ namespace Oqtane.Pages
|
||||||
string downloadName = file.Name;
|
string downloadName = file.Name;
|
||||||
string filepath = _files.GetFilePath(file);
|
string filepath = _files.GetFilePath(file);
|
||||||
|
|
||||||
bool hasWidthParam = Request.Query.TryGetValue("width", out var widthStr);
|
var etagValue = file.ModifiedOn.Ticks ^ file.Size;
|
||||||
bool hasHeightParam = Request.Query.TryGetValue("height", out var heightStr);
|
|
||||||
|
bool isRequestingImageManipulation = false;
|
||||||
|
|
||||||
int width = 0;
|
int width = 0;
|
||||||
int height = 0;
|
int height = 0;
|
||||||
|
if (Request.Query.TryGetValue("width", out var widthStr) && int.TryParse(widthStr, out width) && width > 0)
|
||||||
bool isRequestingImageResize =
|
|
||||||
hasWidthParam && int.TryParse(widthStr, out width) && width > 0 &&
|
|
||||||
hasHeightParam && int.TryParse(heightStr, out height) && height > 0;
|
|
||||||
|
|
||||||
if (isRequestingImageResize)
|
|
||||||
{
|
{
|
||||||
etag = Convert.ToString(file.ModifiedOn.Ticks ^ file.Size ^ (width * 31) ^ (height * 17), 16);
|
isRequestingImageManipulation = true;
|
||||||
|
etagValue ^= (width * 31);
|
||||||
}
|
}
|
||||||
else
|
if (Request.Query.TryGetValue("height", out var heightStr) && int.TryParse(heightStr, out height) && height > 0)
|
||||||
{
|
{
|
||||||
etag = Convert.ToString(file.ModifiedOn.Ticks ^ file.Size, 16);
|
isRequestingImageManipulation = true;
|
||||||
|
etagValue ^= (height * 17);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Request.Query.TryGetValue("mode", out var mode);
|
||||||
|
Request.Query.TryGetValue("position", out var position);
|
||||||
|
Request.Query.TryGetValue("background", out var background);
|
||||||
|
|
||||||
|
if (width > 0 || height > 0)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(mode)) etagValue ^= mode.ToString().GetHashCode();
|
||||||
|
if (!string.IsNullOrWhiteSpace(position)) etagValue ^= position.ToString().GetHashCode();
|
||||||
|
if (!string.IsNullOrWhiteSpace(background)) etagValue ^= background.ToString().GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
int rotate;
|
||||||
|
if (Request.Query.TryGetValue("rotate", out var rotateStr) && int.TryParse(rotateStr, out rotate) && 360 > rotate && rotate > 0)
|
||||||
|
{
|
||||||
|
isRequestingImageManipulation = true;
|
||||||
|
etagValue ^= (rotate * 13);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Request.Query.TryGetValue("format", out var format) && _imageService.GetAvailableFormats().Contains(format.ToString()))
|
||||||
|
{
|
||||||
|
isRequestingImageManipulation = true;
|
||||||
|
etagValue ^= format.ToString().GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
etag = Convert.ToString(etagValue, 16);
|
||||||
|
|
||||||
var header = "";
|
var header = "";
|
||||||
if (HttpContext.Request.Headers.TryGetValue(HeaderNames.IfNoneMatch, out var ifNoneMatch))
|
if (HttpContext.Request.Headers.TryGetValue(HeaderNames.IfNoneMatch, out var ifNoneMatch))
|
||||||
{
|
{
|
||||||
|
@ -160,7 +193,7 @@ namespace Oqtane.Pages
|
||||||
return BrokenFile();
|
return BrokenFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isRequestingImageResize)
|
if (isRequestingImageManipulation)
|
||||||
{
|
{
|
||||||
var _ImageFiles = _settingRepository.GetSetting(EntityNames.Site, _alias.SiteId, "ImageFiles")?.SettingValue;
|
var _ImageFiles = _settingRepository.GetSetting(EntityNames.Site, _alias.SiteId, "ImageFiles")?.SettingValue;
|
||||||
_ImageFiles = (string.IsNullOrEmpty(_ImageFiles)) ? Constants.ImageFiles : _ImageFiles;
|
_ImageFiles = (string.IsNullOrEmpty(_ImageFiles)) ? Constants.ImageFiles : _ImageFiles;
|
||||||
|
@ -172,22 +205,24 @@ namespace Oqtane.Pages
|
||||||
return BrokenFile();
|
return BrokenFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
Request.Query.TryGetValue("mode", out var mode);
|
|
||||||
Request.Query.TryGetValue("position", out var position);
|
|
||||||
Request.Query.TryGetValue("background", out var background);
|
|
||||||
Request.Query.TryGetValue("rotate", out var rotate);
|
|
||||||
Request.Query.TryGetValue("recreate", out var recreate);
|
Request.Query.TryGetValue("recreate", out var recreate);
|
||||||
|
|
||||||
if (!bool.TryParse(recreate, out _)) recreate = "false";
|
if (!bool.TryParse(recreate, out _)) recreate = "false";
|
||||||
|
if (!_imageService.GetAvailableFormats().Contains(format.ToString())) format = "png";
|
||||||
|
if (width == 0 && height == 0)
|
||||||
|
{
|
||||||
|
width = file.ImageWidth;
|
||||||
|
height = file.ImageHeight;
|
||||||
|
}
|
||||||
|
|
||||||
string imagepath = filepath.Replace(Path.GetExtension(filepath), "." + width.ToString() + "x" + height.ToString() + ".png");
|
string imagepath = filepath.Replace(Path.GetExtension(filepath), "." + width.ToString() + "x" + height.ToString() + "." + format);
|
||||||
if (!System.IO.File.Exists(imagepath) || bool.Parse(recreate))
|
if (!System.IO.File.Exists(imagepath) || bool.Parse(recreate))
|
||||||
{
|
{
|
||||||
// user has edit access to folder or folder supports the image size being created
|
// user has edit access to folder or folder supports the image size being created
|
||||||
if (_userPermissions.IsAuthorized(User, PermissionNames.Edit, file.Folder.PermissionList) ||
|
if (_userPermissions.IsAuthorized(User, PermissionNames.Edit, file.Folder.PermissionList) ||
|
||||||
(!string.IsNullOrEmpty(file.Folder.ImageSizes) && (file.Folder.ImageSizes == "*" || file.Folder.ImageSizes.ToLower().Split(",").Contains(width.ToString() + "x" + height.ToString()))))
|
(!string.IsNullOrEmpty(file.Folder.ImageSizes) && (file.Folder.ImageSizes == "*" || file.Folder.ImageSizes.ToLower().Split(",").Contains(width.ToString() + "x" + height.ToString()))))
|
||||||
{
|
{
|
||||||
imagepath = _imageService.CreateImage(filepath, width, height, mode, position, background, rotate, imagepath);
|
imagepath = _imageService.CreateImage(filepath, width, height, mode, position, background, rotateStr, format, imagepath);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -204,7 +239,7 @@ namespace Oqtane.Pages
|
||||||
return BrokenFile();
|
return BrokenFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadName = file.Name.Replace(Path.GetExtension(filepath), "." + width.ToString() + "x" + height.ToString() + ".png");
|
downloadName = file.Name.Replace(Path.GetExtension(filepath), "." + width.ToString() + "x" + height.ToString() + "." + format);
|
||||||
filepath = imagepath;
|
filepath = imagepath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,21 +5,29 @@ using System.IO;
|
||||||
using System;
|
using System;
|
||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
using Oqtane.Infrastructure;
|
using Oqtane.Infrastructure;
|
||||||
using Oqtane.Interfaces;
|
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
|
using SixLabors.ImageSharp.Formats;
|
||||||
|
using SixLabors.ImageSharp.Formats.Webp;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Oqtane.Services
|
namespace Oqtane.Services
|
||||||
{
|
{
|
||||||
public class ImageService : IImageService
|
public class ImageService : IImageService
|
||||||
{
|
{
|
||||||
private readonly ILogManager _logger;
|
private readonly ILogManager _logger;
|
||||||
|
private static readonly string[] _formats = ["png", "webp"];
|
||||||
|
|
||||||
public ImageService(ILogManager logger)
|
public ImageService(ILogManager logger)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string CreateImage(string filepath, int width, int height, string mode, string position, string background, string rotate, string imagepath)
|
public string[] GetAvailableFormats()
|
||||||
|
{
|
||||||
|
return _formats;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CreateImage(string filepath, int width, int height, string mode, string position, string background, string rotate, string format, string imagepath)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -29,6 +37,7 @@ namespace Oqtane.Services
|
||||||
if (!Color.TryParseHex("#" + background, out _)) background = "transparent";
|
if (!Color.TryParseHex("#" + background, out _)) background = "transparent";
|
||||||
if (!int.TryParse(rotate, out _)) rotate = "0";
|
if (!int.TryParse(rotate, out _)) rotate = "0";
|
||||||
rotate = (int.Parse(rotate) < 0 || int.Parse(rotate) > 360) ? "0" : rotate;
|
rotate = (int.Parse(rotate) < 0 || int.Parse(rotate) > 360) ? "0" : rotate;
|
||||||
|
if (!_formats.Contains(format)) format = "png";
|
||||||
|
|
||||||
using (var stream = new FileStream(filepath, FileMode.Open, FileAccess.Read))
|
using (var stream = new FileStream(filepath, FileMode.Open, FileAccess.Read))
|
||||||
{
|
{
|
||||||
|
@ -39,43 +48,34 @@ namespace Oqtane.Services
|
||||||
Enum.TryParse(mode, true, out ResizeMode resizemode);
|
Enum.TryParse(mode, true, out ResizeMode resizemode);
|
||||||
Enum.TryParse(position, true, out AnchorPositionMode anchorpositionmode);
|
Enum.TryParse(position, true, out AnchorPositionMode anchorpositionmode);
|
||||||
|
|
||||||
PngEncoder encoder;
|
if (width == 0 && height == 0)
|
||||||
|
{
|
||||||
|
width = image.Width;
|
||||||
|
height = image.Height;
|
||||||
|
}
|
||||||
|
|
||||||
|
IImageEncoder encoder;
|
||||||
|
var resizeOptions = new ResizeOptions
|
||||||
|
{
|
||||||
|
Mode = resizemode,
|
||||||
|
Position = anchorpositionmode,
|
||||||
|
Size = new Size(width, height)
|
||||||
|
};
|
||||||
|
|
||||||
if (background != "transparent")
|
if (background != "transparent")
|
||||||
{
|
{
|
||||||
image.Mutate(x => x
|
resizeOptions.PadColor = Color.ParseHex("#" + background);
|
||||||
.AutoOrient() // auto orient the image
|
encoder = GetEncoder(format, transparent: false);
|
||||||
.Rotate(angle)
|
|
||||||
.Resize(new ResizeOptions
|
|
||||||
{
|
|
||||||
Mode = resizemode,
|
|
||||||
Position = anchorpositionmode,
|
|
||||||
Size = new Size(width, height),
|
|
||||||
PadColor = Color.ParseHex("#" + background)
|
|
||||||
}));
|
|
||||||
|
|
||||||
encoder = new PngEncoder();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
image.Mutate(x => x
|
encoder = GetEncoder(format, transparent: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
image.Mutate(x => x
|
||||||
.AutoOrient() // auto orient the image
|
.AutoOrient() // auto orient the image
|
||||||
.Rotate(angle)
|
.Rotate(angle)
|
||||||
.Resize(new ResizeOptions
|
.Resize(resizeOptions));
|
||||||
{
|
|
||||||
Mode = resizemode,
|
|
||||||
Position = anchorpositionmode,
|
|
||||||
Size = new Size(width, height)
|
|
||||||
}));
|
|
||||||
|
|
||||||
encoder = new PngEncoder
|
|
||||||
{
|
|
||||||
ColorType = PngColorType.RgbWithAlpha,
|
|
||||||
TransparentColorMode = PngTransparentColorMode.Preserve,
|
|
||||||
BitDepth = PngBitDepth.Bit8,
|
|
||||||
CompressionLevel = PngCompressionLevel.BestSpeed
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
image.Save(imagepath, encoder);
|
image.Save(imagepath, encoder);
|
||||||
}
|
}
|
||||||
|
@ -89,5 +89,36 @@ namespace Oqtane.Services
|
||||||
|
|
||||||
return imagepath;
|
return imagepath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static IImageEncoder GetEncoder(string format, bool transparent)
|
||||||
|
{
|
||||||
|
return format switch
|
||||||
|
{
|
||||||
|
"png" => GetPngEncoder(transparent),
|
||||||
|
"webp" => GetWebpEncoder(transparent),
|
||||||
|
_ => GetPngEncoder(transparent),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PngEncoder GetPngEncoder(bool transparent)
|
||||||
|
{
|
||||||
|
return new PngEncoder()
|
||||||
|
{
|
||||||
|
ColorType = transparent ? PngColorType.RgbWithAlpha : PngColorType.Rgb,
|
||||||
|
TransparentColorMode = transparent ? PngTransparentColorMode.Preserve : PngTransparentColorMode.Clear,
|
||||||
|
BitDepth = PngBitDepth.Bit8,
|
||||||
|
CompressionLevel = PngCompressionLevel.BestSpeed
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static WebpEncoder GetWebpEncoder(bool transparent)
|
||||||
|
{
|
||||||
|
return new WebpEncoder()
|
||||||
|
{
|
||||||
|
FileFormat = WebpFileFormatType.Lossy,
|
||||||
|
Quality = 60,
|
||||||
|
TransparentColorMode = transparent ? WebpTransparentColorMode.Preserve : WebpTransparentColorMode.Clear,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ namespace Oqtane.Services
|
||||||
{
|
{
|
||||||
public interface IImageService
|
public interface IImageService
|
||||||
{
|
{
|
||||||
public string CreateImage(string filepath, int width, int height, string mode, string position, string background, string rotate, string imagepath);
|
public string[] GetAvailableFormats();
|
||||||
|
|
||||||
|
public string CreateImage(string filepath, int width, int height, string mode, string position, string background, string rotate, string format, string imagepath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user