requests implemented

pull/16632/head
Salih 2 years ago
parent 91abac44fa
commit 4186e7763d

@ -441,7 +441,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Ddd.Domain.Shared"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Image.Abstractions", "src\Volo.Abp.Image.Abstractions\Volo.Abp.Image.Abstractions.csproj", "{32F3E84B-D02E-42BD-BC5C-0D211564EF30}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Image.Web", "src\Volo.Abp.Image.Web\Volo.Abp.Image.Web.csproj", "{78340A37-219E-4F2D-9AC6-40A7B467EEEC}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Image.AspNetCore", "src\Volo.Abp.Image.AspNetCore\Volo.Abp.Image.AspNetCore.csproj", "{78340A37-219E-4F2D-9AC6-40A7B467EEEC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Image.ImageSharp", "src\Volo.Abp.Image.ImageSharp\Volo.Abp.Image.ImageSharp.csproj", "{44467427-E0BE-492C-B9B4-82B362C183C3}"
EndProject

@ -1,15 +1,21 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace Volo.Abp.Image;
public interface IImageCompressor // TODO: RENAME: IImageCompressorContributor
public interface IImageCompressor
{
//TODO: new extension method that works with byte arrays
Task<Stream> CompressAsync(Stream stream, CancellationToken cancellationToken = default); // TODO: TryCompressAsync & remove CanCompress
Stream Compress(Stream stream);
bool CanCompress(IImageFormat imageFormat);
Task<ImageProcessResult<Stream>> CompressAsync(
Stream stream,
[CanBeNull] string mimeType = null,
bool ignoreExceptions = false,
CancellationToken cancellationToken = default);
Task<ImageProcessResult<byte[]>> CompressAsync(
byte[] bytes,
[CanBeNull] string mimeType = null,
bool ignoreExceptions = false,
CancellationToken cancellationToken = default);
}

@ -0,0 +1,11 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace Volo.Abp.Image;
public interface IImageCompressorContributor
{
Task<ImageContributorResult<Stream>> TryCompressAsync(Stream stream, string mimeType = null, CancellationToken cancellationToken = default);
Task<ImageContributorResult<byte[]>> TryCompressAsync(byte[] bytes, string mimeType = null, CancellationToken cancellationToken = default);
}

@ -1,14 +0,0 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace Volo.Abp.Image;
public interface IImageCompressorManager // TODO: Rename to IImageCompressor
{
Task<Stream> CompressAsync(
Stream stream,
[CanBeNull] IImageFormat imageFormat = null,
CancellationToken cancellationToken = default);
}

@ -1,6 +0,0 @@
namespace Volo.Abp.Image;
public interface IImageCompressorSelector //TODO: Remove, merge to IImageCompressorManager
{
IImageCompressor FindCompressor(IImageFormat imageFormat);
}

@ -1,6 +0,0 @@
namespace Volo.Abp.Image;
public interface IImageFormat
{
string MimeType { get; }
}

@ -1,8 +0,0 @@
using System.IO;
namespace Volo.Abp.Image;
public interface IImageFormatDetector
{
IImageFormat FindFormat(Stream image);
}

@ -1,9 +0,0 @@
namespace Volo.Abp.Image;
public interface IImageResizeParameter //TODO: Remove
{
int? Width { get; }
int? Height { get; }
ImageResizeMode? Mode { get; }
}

@ -1,15 +1,13 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace Volo.Abp.Image;
public interface IImageResizer
{
Task<Stream> ResizeAsync(Stream stream, IImageResizeParameter resizeParameter,
CancellationToken cancellationToken = default); //TODO: TryResizeAsync...
Task<ImageProcessResult<Stream>> ResizeAsync(Stream stream, ImageResizeArgs resizeArgs, [CanBeNull]string mimeType = null, CancellationToken cancellationToken = default);
Stream Resize(Stream stream, IImageResizeParameter resizeParameter); //TODO: Remove
bool CanResize(IImageFormat imageFormat); //TODO: Discard (merge with TryResizeAsync)
Task<ImageProcessResult<byte[]>> ResizeAsync(byte[] bytes, ImageResizeArgs resizeArgs, [CanBeNull] string mimeType = null, CancellationToken cancellationToken = default);
}

@ -0,0 +1,12 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace Volo.Abp.Image;
public interface IImageResizerContributor
{
Task<ImageContributorResult<Stream>> TryResizeAsync(Stream stream, ImageResizeArgs resizeArgs, string mimeType = null, CancellationToken cancellationToken = default);
Task<ImageContributorResult<byte[]>> TryResizeAsync(byte[] bytes, ImageResizeArgs resizeArgs, string mimeType = null, CancellationToken cancellationToken = default);
}

@ -1,6 +0,0 @@
namespace Volo.Abp.Image;
public interface IImageResizerSelector
{
IImageResizer FindResizer(IImageFormat imageFormat);
}

@ -0,0 +1,69 @@
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Image;
public class ImageCompressor : IImageCompressor, ITransientDependency
{
protected IEnumerable<IImageCompressorContributor> ImageCompressorContributors { get; }
public ImageCompressor(IEnumerable<IImageCompressorContributor> imageCompressorContributors)
{
ImageCompressorContributors = imageCompressorContributors;
}
public virtual async Task<ImageProcessResult<Stream>> CompressAsync(Stream stream, string mimeType = null, bool ignoreExceptions = false, CancellationToken cancellationToken = default)
{
foreach (var imageCompressorContributor in ImageCompressorContributors)
{
var result = await imageCompressorContributor.TryCompressAsync(stream, mimeType, cancellationToken);
if (!result.IsSupported)
{
continue;
}
if (!ignoreExceptions && result.Exception != null)
{
throw result.Exception;
}
if (result.IsSuccess)
{
return new ImageProcessResult<Stream>(result.Result, result.IsSuccess);
}
return new ImageProcessResult<Stream>(stream, false);
}
return new ImageProcessResult<Stream>(stream, false);
}
public virtual async Task<ImageProcessResult<byte[]>> CompressAsync(byte[] bytes, string mimeType = null, bool ignoreExceptions = false, CancellationToken cancellationToken = default)
{
foreach (var imageCompressorContributor in ImageCompressorContributors)
{
var result = await imageCompressorContributor.TryCompressAsync(bytes, mimeType, cancellationToken);
if (!result.IsSupported)
{
continue;
}
if (!ignoreExceptions && result.Exception != null)
{
throw result.Exception;
}
if (result.IsSuccess)
{
return new ImageProcessResult<byte[]>(result.Result, result.IsSuccess);
}
return new ImageProcessResult<byte[]>(bytes, false);
}
return new ImageProcessResult<byte[]>(bytes, false);
}
}

@ -0,0 +1,19 @@
using System;
namespace Volo.Abp.Image;
public class ImageContributorResult<T>
{
public T Result { get; }
public bool IsSuccess { get; }
public bool IsSupported { get; }
public Exception Exception { get; }
public ImageContributorResult(T result, bool isSuccess, bool isSupported = true, Exception exception = null)
{
Result = result;
IsSuccess = isSuccess;
IsSupported = isSupported;
Exception = exception;
}
}

@ -1,11 +0,0 @@
namespace Volo.Abp.Image;
public class ImageFormat : IImageFormat
{
public ImageFormat(string mimeType)
{
MimeType = mimeType;
}
public string MimeType { get; }
}

@ -0,0 +1,13 @@
namespace Volo.Abp.Image;
public class ImageProcessResult<T>
{
public T Result { get; }
public bool IsSuccess { get; }
public ImageProcessResult(T result, bool isSuccess)
{
Result = result;
IsSuccess = isSuccess;
}
}

@ -0,0 +1,33 @@
using System;
namespace Volo.Abp.Image;
public class ImageResizeArgs
{
public int Width { get; set; }
public int Height { get; set; }
public ImageResizeMode Mode { get; set; } = ImageResizeMode.Default;
public ImageResizeArgs(int? width = null, int? height = null, ImageResizeMode? mode = null)
{
if (mode.HasValue)
{
Mode = mode.Value;
}
if (width is < 0)
{
throw new ArgumentException("Width cannot be negative!", nameof(width));
}
if (height is < 0)
{
throw new ArgumentException("Height cannot be negative!", nameof(height));
}
Width = width ?? 0;
Height = height ?? 0;
}
}

@ -9,7 +9,5 @@ public enum ImageResizeMode
Max,
Crop,
Pad,
Fill,
Distort,
Default = Stretch
Default
}

@ -0,0 +1,6 @@
namespace Volo.Abp.Image;
public class ImageResizeOptions
{
public ImageResizeMode DefaultResizeMode { get; set; } = ImageResizeMode.None;
}

@ -0,0 +1,85 @@
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Image;
public class ImageResizer : IImageResizer, ITransientDependency
{
protected IEnumerable<IImageResizerContributor> ImageResizerContributors { get; }
protected ImageResizeOptions ImageResizeOptions { get; }
public ImageResizer(IEnumerable<IImageResizerContributor> imageResizerContributors, IOptions<ImageResizeOptions> imageResizeOptions)
{
ImageResizerContributors = imageResizerContributors;
ImageResizeOptions = imageResizeOptions.Value;
}
public async Task<ImageProcessResult<Stream>> ResizeAsync(Stream stream, ImageResizeArgs resizeArgs, string mimeType = null, CancellationToken cancellationToken = default)
{
ChangeDefaultResizeMode(resizeArgs);
foreach (var imageResizerContributor in ImageResizerContributors)
{
var result = await imageResizerContributor.TryResizeAsync(stream, resizeArgs, mimeType, cancellationToken);
if (!result.IsSupported)
{
continue;
}
if (result.Exception != null)
{
throw result.Exception;
}
if (result.IsSuccess)
{
return new ImageProcessResult<Stream>(result.Result, result.IsSuccess);
}
return new ImageProcessResult<Stream>(stream, false);
}
return new ImageProcessResult<Stream>(stream, false);
}
public async Task<ImageProcessResult<byte[]>> ResizeAsync(byte[] bytes, ImageResizeArgs resizeArgs, string mimeType = null, CancellationToken cancellationToken = default)
{
ChangeDefaultResizeMode(resizeArgs);
foreach (var imageResizerContributor in ImageResizerContributors)
{
var result = await imageResizerContributor.TryResizeAsync(bytes, resizeArgs, mimeType, cancellationToken);
if (!result.IsSupported)
{
continue;
}
if (result.Exception != null)
{
throw result.Exception;
}
if (result.IsSuccess)
{
return new ImageProcessResult<byte[]>(result.Result, result.IsSuccess);
}
return new ImageProcessResult<byte[]>(bytes, false);
}
return new ImageProcessResult<byte[]>(bytes, false);
}
protected virtual void ChangeDefaultResizeMode(ImageResizeArgs resizeArgs)
{
if (resizeArgs.Mode == ImageResizeMode.Default)
{
resizeArgs.Mode = ImageResizeOptions.DefaultResizeMode;
}
}
}

@ -5,7 +5,7 @@
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<PackageId>Volo.Abp.Image.Web</PackageId>
<PackageId>Volo.Abp.Image.AspNetCore</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
@ -17,15 +17,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.AspNetCore\Volo.Abp.AspNetCore.csproj" />
<ProjectReference Include="..\Volo.Abp.Image\Volo.Abp.Image.csproj" />
</ItemGroup>
<ItemGroup>
<Content Update="Volo.Abp.Json.abppkg.json">
<Pack>true</Pack>
<PackagePath>content\</PackagePath>
</Content>
<ProjectReference Include="..\Volo.Abp.Image.Abstractions\Volo.Abp.Image.Abstractions.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,9 @@
using Volo.Abp.Modularity;
namespace Volo.Abp.Image;
[DependsOn(typeof(AbpImageAbstractionsModule))]
public class AbpImageAspNetCoreModule : AbpModule
{
}

@ -0,0 +1,104 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Content;
namespace Volo.Abp.Image;
public class CompressImageAttribute : ActionFilterAttribute
{
public string[] Parameters { get; }
protected IImageCompressor ImageCompressor { get; set; }
public CompressImageAttribute(params string[] parameters)
{
Parameters = parameters;
}
public async override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var parameters = Parameters.Any()
? context.ActionArguments.Where(x => Parameters.Contains(x.Key)).ToArray()
: context.ActionArguments.ToArray();
ImageCompressor = context.HttpContext.RequestServices.GetRequiredService<IImageCompressor>();
foreach (var (key, value) in parameters)
{
object compressedValue = value switch {
IFormFile file => await CompressImageAsync(file),
IRemoteStreamContent remoteStreamContent => await CompressImageAsync(remoteStreamContent),
Stream stream => await CompressImageAsync(stream),
IEnumerable<byte> bytes => await CompressImageAsync(bytes.ToArray()),
_ => null
};
if (compressedValue != null)
{
context.ActionArguments[key] = compressedValue;
}
}
await next();
}
protected async Task<IFormFile> CompressImageAsync(IFormFile file)
{
if(file.ContentType == null || !file.ContentType.StartsWith("image/"))
{
return file;
}
var result = await ImageCompressor.CompressAsync(file.OpenReadStream(), file.ContentType);
if (result.IsSuccess)
{
return new FormFile(result.Result, 0, result.Result.Length, file.Name, file.FileName);
}
return file;
}
protected async Task<IRemoteStreamContent> CompressImageAsync(IRemoteStreamContent remoteStreamContent)
{
if(remoteStreamContent.ContentType == null || !remoteStreamContent.ContentType.StartsWith("image/"))
{
return remoteStreamContent;
}
var result = await ImageCompressor.CompressAsync(remoteStreamContent.GetStream(), remoteStreamContent.ContentType);
if (result.IsSuccess)
{
var fileName = remoteStreamContent.FileName;
var contentType = remoteStreamContent.ContentType;
remoteStreamContent.Dispose();
return new RemoteStreamContent(result.Result, fileName, contentType);
}
return remoteStreamContent;
}
protected async Task<Stream> CompressImageAsync(Stream stream)
{
var result = await ImageCompressor.CompressAsync(stream);
if (result.IsSuccess)
{
await stream.DisposeAsync();
return result.Result;
}
return stream;
}
protected async Task<byte[]> CompressImageAsync(byte[] bytes)
{
return (await ImageCompressor.CompressAsync(bytes)).Result;
}
}

@ -0,0 +1,117 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Content;
namespace Volo.Abp.Image;
public class ResizeImageAttribute : ActionFilterAttribute
{
public int? Width { get; }
public int? Height { get; }
public ImageResizeMode Mode { get; set; }
public string[] Parameters { get; }
protected IImageResizer ImageResizer { get; set; }
public ResizeImageAttribute(int width, int height, params string[] parameters)
{
Width = width;
Height = height;
Parameters = parameters;
}
public ResizeImageAttribute(int size, params string[] parameters)
{
Width = size;
Height = size;
Parameters = parameters;
}
public async override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var parameters = Parameters.Any()
? context.ActionArguments.Where(x => Parameters.Contains(x.Key)).ToArray()
: context.ActionArguments.ToArray();
ImageResizer = context.HttpContext.RequestServices.GetRequiredService<IImageResizer>();
foreach (var (key, value) in parameters)
{
object resizedValue = value switch {
IFormFile file => await ResizeImageAsync(file),
IRemoteStreamContent remoteStreamContent => await ResizeImageAsync(remoteStreamContent),
Stream stream => await ResizeImageAsync(stream),
IEnumerable<byte> bytes => await ResizeImageAsync(bytes.ToArray()),
_ => null
};
if (resizedValue != null)
{
context.ActionArguments[key] = resizedValue;
}
}
await next();
}
protected async Task<IFormFile> ResizeImageAsync(IFormFile file)
{
if(file.ContentType == null || !file.ContentType.StartsWith("image/"))
{
return file;
}
var result = await ImageResizer.ResizeAsync(file.OpenReadStream(), new ImageResizeArgs(Width, Height, Mode), file.ContentType);
if (result.IsSuccess)
{
return new FormFile(result.Result, 0, result.Result.Length, file.Name, file.FileName);
}
return file;
}
protected async Task<IRemoteStreamContent> ResizeImageAsync(IRemoteStreamContent remoteStreamContent)
{
if(remoteStreamContent.ContentType == null || !remoteStreamContent.ContentType.StartsWith("image/"))
{
return remoteStreamContent;
}
var result = await ImageResizer.ResizeAsync(remoteStreamContent.GetStream(), new ImageResizeArgs(Width, Height, Mode), remoteStreamContent.ContentType);
if (result.IsSuccess)
{
var fileName = remoteStreamContent.FileName;
var contentType = remoteStreamContent.ContentType;
remoteStreamContent.Dispose();
return new RemoteStreamContent(result.Result, fileName, contentType);
}
return remoteStreamContent;
}
protected async Task<Stream> ResizeImageAsync(Stream stream)
{
var result = await ImageResizer.ResizeAsync(stream, new ImageResizeArgs(Width, Height, Mode));
if (result.IsSuccess)
{
await stream.DisposeAsync();
return result.Result;
}
return stream;
}
protected async Task<byte[]> ResizeImageAsync(byte[] bytes)
{
return (await ImageResizer.ResizeAsync(bytes, new ImageResizeArgs(Width, Height, Mode))).Result;
}
}

@ -0,0 +1,11 @@
using SixLabors.ImageSharp.Formats.Png;
namespace Volo.Abp.Image;
public class ImageSharpCompressOptions
{
public int JpegQuality { get; set; } = 60;
public PngCompressionLevel PngCompressionLevel { get; set; } = PngCompressionLevel.BestCompression;
public bool PngIgnoreMetadata { get; set; } = true;
public int WebpQuality { get; set; } = 60;
}

@ -1,91 +0,0 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Webp;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Image;
public class ImageSharpImageCompressor : IImageCompressor, ITransientDependency
{
public async Task<Stream> CompressAsync(Stream stream, CancellationToken cancellationToken = default)
{
var memoryStream = await stream.CreateMemoryStreamAsync(cancellationToken: cancellationToken);
using var image = await SixLabors.ImageSharp.Image.LoadAsync(memoryStream, cancellationToken);
memoryStream.Position = 0;
var format = await SixLabors.ImageSharp.Image.DetectFormatAsync(memoryStream, cancellationToken);
memoryStream.Position = 0;
var encoder = image.GetConfiguration().ImageFormatsManager.FindEncoder(format);
switch (encoder)
{
case JpegEncoder jpegEncoder:
jpegEncoder.Quality = 60;
break;
case PngEncoder pngEncoder:
pngEncoder.CompressionLevel = PngCompressionLevel.BestCompression; //TODO: AbpImageSharpOptions (others too)
pngEncoder.IgnoreMetadata = true;
break;
case WebpEncoder webPEncoder:
webPEncoder.Quality = 60;
webPEncoder.UseAlphaCompression = true;
break;
case null:
throw new NotSupportedException($"No encoder available for the given format: {format.Name}");
}
await image.SaveAsync(memoryStream, encoder, cancellationToken: cancellationToken);
memoryStream.SetLength(memoryStream.Position);
return memoryStream;
}
public Stream Compress(Stream stream)
{
var newStream = stream.CreateMemoryStream();
using var image = SixLabors.ImageSharp.Image.Load(newStream);
newStream.Position = 0;
var format = SixLabors.ImageSharp.Image.DetectFormat(newStream);
newStream.Position = 0;
var encoder = image.GetConfiguration().ImageFormatsManager.FindEncoder(format);
switch (encoder)
{
case JpegEncoder jpegEncoder:
jpegEncoder.Quality = 60;
break;
case PngEncoder pngEncoder:
pngEncoder.CompressionLevel = PngCompressionLevel.BestCompression;
pngEncoder.IgnoreMetadata = true;
break;
case WebpEncoder webPEncoder:
webPEncoder.Quality = 60;
webPEncoder.UseAlphaCompression = true;
break;
case null:
throw new NotSupportedException($"No encoder available for provided path: {format.Name}");
}
image.Save(newStream, encoder);
newStream.SetLength(newStream.Position);
return newStream;
}
public bool CanCompress(IImageFormat format)
{
//TODO: Use MimeTypes (after moving it to Volo.Abp.Core)
return format?.MimeType switch {
"image/jpeg" => true,
"image/png" => true,
"image/webp" => true,
_ => false
};
}
}

@ -0,0 +1,108 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Webp;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http;
namespace Volo.Abp.Image;
public class ImageSharpImageCompressorContributor : IImageCompressorContributor, ITransientDependency
{
protected ImageSharpCompressOptions Options { get; }
public ImageSharpImageCompressorContributor(IOptions<ImageSharpCompressOptions> options)
{
Options = options.Value;
}
public async Task<ImageContributorResult<Stream>> TryCompressAsync(Stream stream, string mimeType = null,
CancellationToken cancellationToken = default)
{
if (!string.IsNullOrWhiteSpace(mimeType) && !CanCompress(mimeType))
{
return new ImageContributorResult<Stream>(stream, false, false);
}
MemoryStream ms = null;
try
{
var (image, format) = await SixLabors.ImageSharp.Image.LoadWithFormatAsync(stream, cancellationToken);
mimeType = format.DefaultMimeType;
if (!CanCompress(mimeType))
{
return new ImageContributorResult<Stream>(stream, false, false);
}
var encoder = image.GetConfiguration().ImageFormatsManager.FindEncoder(format);
switch (encoder)
{
case JpegEncoder jpegEncoder:
jpegEncoder.Quality = Options.JpegQuality;
break;
case PngEncoder pngEncoder:
pngEncoder.CompressionLevel = Options.PngCompressionLevel;
pngEncoder.IgnoreMetadata = Options.PngIgnoreMetadata;
break;
case WebpEncoder webPEncoder:
webPEncoder.Quality = Options.WebpQuality;
webPEncoder.UseAlphaCompression = true;
break;
case null:
throw new NotSupportedException($"No encoder available for the given format: {format.Name}");
}
ms = new MemoryStream();
await image.SaveAsync(ms, encoder, cancellationToken: cancellationToken);
ms.Position = 0;
return new ImageContributorResult<Stream>(ms, true);
}
catch (Exception e)
{
ms?.Dispose();
return new ImageContributorResult<Stream>(stream, false, true, e);
}
}
public async Task<ImageContributorResult<byte[]>> TryCompressAsync(byte[] bytes, string mimeType = null,
CancellationToken cancellationToken = default)
{
if (!string.IsNullOrWhiteSpace(mimeType) && !CanCompress(mimeType))
{
return new ImageContributorResult<byte[]>(bytes, false, false);
}
using var ms = new MemoryStream(bytes);
var result = await TryCompressAsync(ms, mimeType, cancellationToken);
if (!result.IsSuccess)
{
return new ImageContributorResult<byte[]>(bytes, result.IsSuccess, result.IsSupported, result.Exception);
}
var newBytes = await result.Result.GetAllBytesAsync(cancellationToken);
result.Result.Dispose();
return new ImageContributorResult<byte[]>(newBytes, true);
}
private static bool CanCompress(string mimeType)
{
return mimeType switch {
MimeTypes.Image.Jpeg => true,
MimeTypes.Image.Png => true,
MimeTypes.Image.Webp => true,
_ => false
};
}
}

@ -1,13 +0,0 @@
using System.IO;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Image;
public class ImageSharpImageFormatDetector : IImageFormatDetector, ITransientDependency
{
public IImageFormat FindFormat(Stream stream)
{
var format = SixLabors.ImageSharp.Image.DetectFormat(stream);
return new ImageFormat(format.DefaultMimeType);
}
}

@ -1,100 +0,0 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Image;
public class ImageSharpImageResizer : IImageResizer, ITransientDependency
{
public async Task<Stream> ResizeAsync(Stream stream, IImageResizeParameter resizeParameter, CancellationToken cancellationToken = default)
{
var newStream = await stream.CreateMemoryStreamAsync(cancellationToken: cancellationToken);
using var image = await SixLabors.ImageSharp.Image.LoadAsync(newStream, cancellationToken);
ApplyMode(image, resizeParameter);
newStream.Position = 0;
var format = await SixLabors.ImageSharp.Image.DetectFormatAsync(newStream, cancellationToken);
newStream.Position = 0;
await image.SaveAsync(newStream, format, cancellationToken: cancellationToken);
newStream.SetLength(newStream.Position);
newStream.Position = 0;
return newStream;
}
public Stream Resize(Stream stream, IImageResizeParameter resizeParameter)
{
var newStream = stream.CreateMemoryStream();
using var image = SixLabors.ImageSharp.Image.Load(newStream);
ApplyMode(image, resizeParameter);
newStream.Position = 0;
var format = SixLabors.ImageSharp.Image.DetectFormat(newStream);
newStream.Position = 0;
image.Save(newStream, format);
newStream.SetLength(newStream.Position);
newStream.Position = 0;
return newStream;
}
public bool CanResize(IImageFormat imageFormat)
{
return imageFormat?.MimeType switch
{
"image/jpeg" => true,
"image/png" => true,
"image/gif" => true,
"image/bmp" => true,
"image/tiff" => true,
_ => false
};
}
private void ApplyMode(SixLabors.ImageSharp.Image image, IImageResizeParameter resizeParameter)
{
var width = resizeParameter.Width ?? image.Width;
var height = resizeParameter.Height ?? image.Height;
var defaultResizeOptions = new ResizeOptions { Size = new SixLabors.ImageSharp.Size(width, height) };
switch (resizeParameter.Mode)
{
case null:
case ImageResizeMode.None:
image.Mutate(x => x.Resize(defaultResizeOptions));
break;
case ImageResizeMode.Stretch:
defaultResizeOptions.Mode = ResizeMode.Stretch;
image.Mutate(x => x.Resize(defaultResizeOptions));
break;
case ImageResizeMode.BoxPad:
defaultResizeOptions.Mode = ResizeMode.BoxPad;
image.Mutate(x => x.Resize(defaultResizeOptions));
break;
case ImageResizeMode.Min:
defaultResizeOptions.Mode = ResizeMode.Min;
image.Mutate(x => x.Resize(defaultResizeOptions));
break;
case ImageResizeMode.Max:
defaultResizeOptions.Mode = ResizeMode.Max;
image.Mutate(x => x.Resize(defaultResizeOptions));
break;
case ImageResizeMode.Crop:
defaultResizeOptions.Mode = ResizeMode.Crop;
image.Mutate(x => x.Resize(defaultResizeOptions));
break;
case ImageResizeMode.Pad:
defaultResizeOptions.Mode = ResizeMode.Pad;
image.Mutate(x => x.Resize(defaultResizeOptions));
break;
case ImageResizeMode.Fill:
defaultResizeOptions.Mode = ResizeMode.Stretch;
image.Mutate(x => x.Resize(defaultResizeOptions));
break;
default:
throw new NotSupportedException("Resize mode " + resizeParameter.Mode + "is not supported!");
}
}
}

@ -0,0 +1,118 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http;
namespace Volo.Abp.Image;
public class ImageSharpImageResizerContributor : IImageResizerContributor, ITransientDependency
{
public async Task<ImageContributorResult<Stream>> TryResizeAsync(Stream stream, ImageResizeArgs resizeArgs,
string mimeType = null,
CancellationToken cancellationToken = default)
{
if (!string.IsNullOrWhiteSpace(mimeType) && !CanResize(mimeType))
{
return new ImageContributorResult<Stream>(stream, false, false);
}
var (image, format) = await SixLabors.ImageSharp.Image.LoadWithFormatAsync(stream, cancellationToken);
mimeType = format.DefaultMimeType;
if (!CanResize(mimeType))
{
return new ImageContributorResult<Stream>(stream, false, false);
}
var size = new Size();
if (resizeArgs.Width > 0)
{
size.Width = resizeArgs.Width;
}
if (resizeArgs.Height > 0)
{
size.Height = resizeArgs.Height;
}
var defaultResizeOptions = new ResizeOptions { Size = size };
MemoryStream ms = null;
try
{
if (ResizeModeMap.TryGetValue(resizeArgs.Mode, out var resizeMode))
{
defaultResizeOptions.Mode = resizeMode;
image.Mutate(x => x.Resize(defaultResizeOptions));
}
else
{
throw new NotSupportedException("Resize mode " + resizeArgs.Mode + "is not supported!");
}
ms = new MemoryStream();
await image.SaveAsync(ms, format, cancellationToken: cancellationToken);
ms.SetLength(ms.Position);
ms.Position = 0;
return new ImageContributorResult<Stream>(ms, true);
}
catch (Exception e)
{
ms?.Dispose();
return new ImageContributorResult<Stream>(stream, false, true, e);
}
}
public async Task<ImageContributorResult<byte[]>> TryResizeAsync(byte[] bytes, ImageResizeArgs resizeArgs,
string mimeType = null,
CancellationToken cancellationToken = default)
{
if (!string.IsNullOrWhiteSpace(mimeType) && !CanResize(mimeType))
{
return new ImageContributorResult<byte[]>(bytes, false, false);
}
using var ms = new MemoryStream(bytes);
var result = await TryResizeAsync(ms, resizeArgs, mimeType, cancellationToken);
if (!result.IsSuccess)
{
return new ImageContributorResult<byte[]>(bytes, result.IsSuccess, result.IsSupported, result.Exception);
}
var newBytes = await result.Result.GetAllBytesAsync(cancellationToken);
result.Result.Dispose();
return new ImageContributorResult<byte[]>(newBytes, true);
}
private static bool CanResize(string mimeType)
{
return mimeType switch {
MimeTypes.Image.Jpeg => true,
MimeTypes.Image.Png => true,
MimeTypes.Image.Gif => true,
MimeTypes.Image.Bmp => true,
MimeTypes.Image.Tiff => true,
_ => false
};
}
private readonly static Dictionary<ImageResizeMode, ResizeMode> ResizeModeMap = new() {
{ ImageResizeMode.None, ResizeMode.Crop },
{ ImageResizeMode.Stretch, ResizeMode.Stretch },
{ ImageResizeMode.BoxPad, ResizeMode.BoxPad },
{ ImageResizeMode.Min, ResizeMode.Min },
{ ImageResizeMode.Max, ResizeMode.Max },
{ ImageResizeMode.Crop, ResizeMode.Crop },
{ ImageResizeMode.Pad, ResizeMode.Pad }
};
}

@ -1,57 +0,0 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using ImageMagick;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Image;
public class MagickImageCompressor : IImageCompressor, ITransientDependency
{
private readonly ImageOptimizer _optimizer = new();
public async Task<Stream> CompressAsync(Stream stream, CancellationToken cancellationToken = default)
{
var newStream = await stream.CreateMemoryStreamAsync(cancellationToken:cancellationToken);
try //TODO: Remove try/catch
{
_optimizer.IsSupported(newStream);
newStream.Position = 0;
_optimizer.Compress(newStream);
}
catch
{
// ignored
}
return newStream;
}
public Stream Compress(Stream stream)
{
var newStream = stream.CreateMemoryStream();
try
{
_optimizer.IsSupported(newStream);
newStream.Position = 0;
_optimizer.Compress(newStream);
}
catch
{
// ignored
}
return newStream;
}
public bool CanCompress(IImageFormat imageFormat)
{
return imageFormat?.MimeType switch
{
"image/jpeg" => true,
"image/png" => true,
"image/gif" => true,
"image/bmp" => true,
"image/tiff" => true,
_ => false
};
}
}

@ -0,0 +1,103 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using ImageMagick;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http;
namespace Volo.Abp.Image;
public class MagickImageCompressorContributor : IImageCompressorContributor, ITransientDependency
{
protected MagickNetCompressOptions Options { get; }
private readonly ImageOptimizer _optimizer;
public MagickImageCompressorContributor(IOptions<MagickNetCompressOptions> options)
{
Options = options.Value;
_optimizer = new ImageOptimizer {
OptimalCompression = Options.OptimalCompression, IgnoreUnsupportedFormats = Options.IgnoreUnsupportedFormats
};
}
private static bool CanCompress(string mimeType)
{
return mimeType switch {
MimeTypes.Image.Jpeg => true,
MimeTypes.Image.Png => true,
MimeTypes.Image.Gif => true,
MimeTypes.Image.Bmp => true,
MimeTypes.Image.Tiff => true,
_ => false
};
}
public async Task<ImageContributorResult<Stream>> TryCompressAsync(Stream stream, string mimeType = null,
CancellationToken cancellationToken = default)
{
if (!string.IsNullOrWhiteSpace(mimeType) && !CanCompress(mimeType))
{
return new ImageContributorResult<Stream>(stream, false, false);
}
MemoryStream ms = null;
try
{
ms = await stream.CreateMemoryStreamAsync(cancellationToken: cancellationToken);
if (!_optimizer.IsSupported(ms))
{
return new ImageContributorResult<Stream>(stream, false, false);
}
Func<Stream, bool> compressFunc;
if (Options.Lossless)
{
compressFunc = _optimizer.LosslessCompress;
}
else
{
compressFunc = _optimizer.Compress;
}
if (compressFunc(ms))
{
return new ImageContributorResult<Stream>(ms, true);
}
ms.Dispose();
return new ImageContributorResult<Stream>(stream, false);
}
catch (Exception e)
{
ms?.Dispose();
return new ImageContributorResult<Stream>(stream, false, false, e);
}
}
public async Task<ImageContributorResult<byte[]>> TryCompressAsync(byte[] bytes, string mimeType = null,
CancellationToken cancellationToken = default)
{
if (!string.IsNullOrWhiteSpace(mimeType) && !CanCompress(mimeType))
{
return new ImageContributorResult<byte[]>(bytes, false, false);
}
using var ms = new MemoryStream(bytes);
var result = await TryCompressAsync(ms, mimeType, cancellationToken);
if (!result.IsSuccess)
{
result.Result.Dispose();
return new ImageContributorResult<byte[]>(bytes, result.IsSuccess, result.IsSupported, result.Exception);
}
var newBytes = await result.Result.GetAllBytesAsync(cancellationToken);
result.Result.Dispose();
return new ImageContributorResult<byte[]>(newBytes, true);
}
}

@ -1,15 +0,0 @@
using System.IO;
using ImageMagick;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Image;
public class MagickImageFormatDetector : IImageFormatDetector, ITransientDependency
{
public IImageFormat FindFormat(Stream image)
{
using var magickImage = new MagickImage(image);
var format = magickImage.FormatInfo;
return format == null ? null : new ImageFormat(format.MimeType ?? string.Empty);
}
}

@ -1,177 +0,0 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using ImageMagick;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Image;
public class MagickImageResizer : IImageResizer, ITransientDependency
{
public async Task<Stream> ResizeAsync(Stream stream, IImageResizeParameter resizeParameter,
CancellationToken cancellationToken = default)
{
var newStream = await stream.CreateMemoryStreamAsync(cancellationToken: cancellationToken);
try //TODO: Remove try/catch
{
using var image = new MagickImage(newStream);
ApplyMode(image, resizeParameter);
newStream.Position = 0;
await image.WriteAsync(newStream, cancellationToken);
newStream.SetLength(newStream.Position);
newStream.Position = 0;
}
catch
{
// ignored
}
return newStream;
}
public Stream Resize(Stream stream, IImageResizeParameter resizeParameter)
{
var newStream = stream.CreateMemoryStream();
try
{
using var image = new MagickImage(newStream);
ApplyMode(image, resizeParameter);
newStream.Position = 0;
image.Write(newStream);
newStream.SetLength(newStream.Position);
newStream.Position = 0;
}
catch
{
// ignored
}
return newStream;
}
public bool CanResize(IImageFormat imageFormat)
{
return imageFormat?.MimeType switch {
"image/jpeg" => true,
"image/png" => true,
"image/gif" => true,
"image/bmp" => true,
"image/tiff" => true,
_ => false
};
}
private void ApplyMode(IMagickImage image, IImageResizeParameter resizeParameter)
{
var width = resizeParameter.Width ?? image.Width;
var height = resizeParameter.Height ?? image.Height;
var defaultMagickGeometry = new MagickGeometry(width, height);
var imageRatio = image.Height / (float)image.Width;
var percentHeight = Math.Abs(height / (float)image.Height);
var percentWidth = Math.Abs(width / (float)image.Width);
var ratio = height / (float)width;
var newWidth = width;
var newHeight = height;
switch (resizeParameter.Mode)
{
case null:
case ImageResizeMode.None:
image.Resize(defaultMagickGeometry);
break;
case ImageResizeMode.Stretch:
defaultMagickGeometry.IgnoreAspectRatio = true;
image.Resize(defaultMagickGeometry);
break;
case ImageResizeMode.Pad:
if (percentHeight < percentWidth)
{
newWidth = (int)Math.Round(image.Width * percentHeight);
}
else
{
newHeight = (int)Math.Round(image.Height * percentWidth);
}
defaultMagickGeometry.IgnoreAspectRatio = true;
image.Resize(newWidth, newHeight);
image.Extent(width, height, Gravity.Center);
break;
case ImageResizeMode.BoxPad:
int boxPadWidth = width > 0 ? width : (int)Math.Round(image.Width * percentHeight);
int boxPadHeight = height > 0 ? height : (int)Math.Round(image.Height * percentWidth);
if (image.Width < boxPadWidth && image.Height < boxPadHeight)
{
newWidth = boxPadWidth;
newHeight = boxPadHeight;
}
image.Resize(newWidth, newHeight);
image.Extent(defaultMagickGeometry, Gravity.Center);
break;
case ImageResizeMode.Max:
if (imageRatio < ratio)
{
newHeight = (int)(image.Height * percentWidth);
}
else
{
newWidth = (int)(image.Width * percentHeight);
}
image.Resize(newWidth, newHeight);
break;
case ImageResizeMode.Min:
if (width > image.Width || height > image.Height)
{
newWidth = image.Width;
newHeight = image.Height;
}
else
{
int widthDiff = image.Width - width;
int heightDiff = image.Height - height;
if (widthDiff > heightDiff)
{
newWidth = (int)Math.Round(height / imageRatio);
}
else if (widthDiff < heightDiff)
{
newHeight = (int)Math.Round(width * imageRatio);
}
else
{
if (height > width)
{
newHeight = (int)Math.Round(image.Height * percentWidth);
}
else
{
newHeight = (int)Math.Round(image.Height * percentWidth);
}
}
}
image.Resize(newWidth, newHeight);
break;
case ImageResizeMode.Crop:
defaultMagickGeometry.IgnoreAspectRatio = true;
image.Crop(width, height, Gravity.Center);
image.Resize(defaultMagickGeometry);
break;
case ImageResizeMode.Distort:
image.Distort(DistortMethod.Resize, width, height);
break;
case ImageResizeMode.Fill:
defaultMagickGeometry.IgnoreAspectRatio = true;
defaultMagickGeometry.FillArea = true;
image.Resize(defaultMagickGeometry);
break;
default:
throw new NotSupportedException("Resize mode " + resizeParameter.Mode + "is not supported!");
}
}
}

@ -0,0 +1,286 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using ImageMagick;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http;
namespace Volo.Abp.Image;
public class MagickImageResizerContributor : IImageResizerContributor, ITransientDependency
{
public async Task<ImageContributorResult<Stream>> TryResizeAsync(Stream stream, ImageResizeArgs resizeArgs,
string mimeType = null,
CancellationToken cancellationToken = default)
{
if (!string.IsNullOrWhiteSpace(mimeType) && !CanResize(mimeType))
{
return new ImageContributorResult<Stream>(stream, false, false);
}
MemoryStream ms = null;
try
{
ms = await stream.CreateMemoryStreamAsync(cancellationToken: cancellationToken);
using var image = new MagickImage(ms);
if (string.IsNullOrWhiteSpace(mimeType))
{
var format = image.FormatInfo;
mimeType = format?.MimeType;
if (!CanResize(mimeType))
{
return new ImageContributorResult<Stream>(stream, false, false);
}
}
Resize(image, resizeArgs);
ms.Position = 0;
await image.WriteAsync(ms, cancellationToken);
ms.SetLength(ms.Position);
ms.Position = 0;
return new ImageContributorResult<Stream>(ms, true);
}
catch (Exception e)
{
ms?.Dispose();
return new ImageContributorResult<Stream>(stream, false, true, e);
}
}
public Task<ImageContributorResult<byte[]>> TryResizeAsync(byte[] bytes, ImageResizeArgs resizeArgs,
string mimeType = null,
CancellationToken cancellationToken = default)
{
if (!string.IsNullOrWhiteSpace(mimeType) && !CanResize(mimeType))
{
return Task.FromResult(new ImageContributorResult<byte[]>(bytes, false, false));
}
try
{
using var image = new MagickImage(bytes);
if (string.IsNullOrWhiteSpace(mimeType))
{
var format = image.FormatInfo;
mimeType = format?.MimeType;
if (!CanResize(mimeType))
{
return Task.FromResult(new ImageContributorResult<byte[]>(bytes, false, false));
}
}
Resize(image, resizeArgs);
return Task.FromResult(new ImageContributorResult<byte[]>(image.ToByteArray(), true));
}
catch (Exception e)
{
return Task.FromResult(new ImageContributorResult<byte[]>(bytes, false, true, e));
}
}
protected virtual bool CanResize(string mimeType)
{
return mimeType switch {
MimeTypes.Image.Jpeg => true,
MimeTypes.Image.Png => true,
MimeTypes.Image.Gif => true,
MimeTypes.Image.Bmp => true,
MimeTypes.Image.Tiff => true,
_ => false
};
}
protected virtual void Resize(MagickImage image, ImageResizeArgs resizeParameter)
{
const int min = 1;
int targetWidth = resizeParameter.Width, targetHeight = resizeParameter.Height;
var sourceWidth = image.Width;
var sourceHeight = image.Height;
if (targetWidth == 0 && targetHeight > 0)
{
targetWidth = Math.Max(min, (int)Math.Round(sourceWidth * targetHeight / (float)sourceHeight));
}
if (targetHeight == 0 && targetWidth > 0)
{
targetHeight = Math.Max(min, (int)Math.Round(sourceHeight * targetWidth / (float)sourceWidth));
}
switch (resizeParameter.Mode)
{
case ImageResizeMode.None:
ResizeModeNone(image, targetWidth, targetHeight);
break;
case ImageResizeMode.Stretch:
ResizeStretch(image, targetWidth, targetHeight);
break;
case ImageResizeMode.Pad:
ResizePad(image, targetWidth, targetHeight);
break;
case ImageResizeMode.BoxPad:
ResizeBoxPad(image, targetWidth, targetHeight);
break;
case ImageResizeMode.Max:
ResizeMax(image, targetWidth, targetHeight);
break;
case ImageResizeMode.Min:
ResizeMin(image, targetWidth, targetHeight);
break;
case ImageResizeMode.Crop:
ResizeCrop(image, targetWidth, targetHeight);
break;
default:
throw new NotSupportedException("Resize mode " + resizeParameter.Mode + "is not supported!");
}
}
protected virtual void ResizeCrop(MagickImage image, int targetWidth, int targetHeight)
{
var defaultMagickGeometry = new MagickGeometry(targetWidth, targetHeight) { IgnoreAspectRatio = true };
image.Crop(defaultMagickGeometry, Gravity.Center);
}
protected virtual void ResizeMin(MagickImage image, int targetWidth, int targetHeight)
{
var sourceWidth = image.Width;
var sourceHeight = image.Height;
var imageRatio = CalculateRatio(sourceWidth, sourceHeight);
var percentWidth = CalculatePercent(sourceWidth, targetWidth);
if (targetWidth > sourceWidth || targetHeight > sourceHeight)
{
targetWidth = sourceWidth;
targetHeight = sourceHeight;
}
else
{
var widthDiff = sourceWidth - targetWidth;
var heightDiff = sourceHeight - targetHeight;
if (widthDiff > heightDiff)
{
targetWidth = (int)Math.Round(targetHeight / imageRatio);
}
else if (widthDiff < heightDiff)
{
targetHeight = (int)Math.Round(targetWidth * imageRatio);
}
else
{
if (targetHeight > targetWidth)
{
targetWidth = (int)Math.Round(sourceHeight * percentWidth);
}
else
{
targetHeight = (int)Math.Round(sourceHeight * percentWidth);
}
}
}
image.Resize(targetWidth, targetHeight);
}
protected virtual void ResizeMax(IMagickImage image, int targetWidth, int targetHeight)
{
var sourceWidth = image.Width;
var sourceHeight = image.Height;
var imageRatio = CalculateRatio(sourceWidth, sourceHeight);
var ratio = CalculateRatio(targetWidth, targetHeight);
var percentHeight = CalculatePercent(sourceHeight, targetHeight);
var percentWidth = CalculatePercent(sourceWidth, targetWidth);
if (imageRatio < ratio)
{
targetHeight = (int)(sourceHeight * percentWidth);
}
else
{
targetWidth = (int)(sourceWidth * percentHeight);
}
image.Resize(targetWidth, targetHeight);
}
protected virtual void ResizeBoxPad(MagickImage image, int targetWidth, int targetHeight)
{
var sourceWidth = image.Width;
var sourceHeight = image.Height;
var percentHeight = CalculatePercent(sourceHeight, targetHeight);
var percentWidth = CalculatePercent(sourceWidth, targetWidth);
var newWidth = targetWidth;
var newHeight = targetHeight;
var boxPadWidth = targetWidth > 0 ? targetWidth : (int)Math.Round(sourceWidth * percentHeight);
var boxPadHeight = targetHeight > 0 ? targetHeight : (int)Math.Round(sourceHeight * percentWidth);
if (sourceWidth < boxPadWidth && sourceHeight < boxPadHeight)
{
newWidth = boxPadWidth;
newHeight = boxPadHeight;
}
image.Resize(newWidth, newHeight);
image.Extent(targetWidth, targetHeight, Gravity.Center, MagickColors.Transparent);
}
protected virtual void ResizePad(MagickImage image, int targetWidth, int targetHeight)
{
var sourceWidth = image.Width;
var sourceHeight = image.Height;
var percentHeight = CalculatePercent(sourceHeight, targetHeight);
var percentWidth = CalculatePercent(sourceWidth, targetWidth);
var newWidth = targetWidth;
var newHeight = targetHeight;
if (percentHeight < percentWidth)
{
newWidth = (int)Math.Round(sourceWidth * percentHeight);
}
else
{
newHeight = (int)Math.Round(sourceHeight * percentWidth);
}
image.Resize(newWidth, newHeight);
image.Extent(targetWidth, targetHeight, Gravity.Center, MagickColors.Transparent);
}
protected virtual float CalculatePercent(int imageHeightOrWidth, int heightOrWidth)
{
return heightOrWidth / (float)imageHeightOrWidth;
}
protected virtual float CalculateRatio(int width, int height)
{
return height / (float)width;
}
protected virtual void ResizeStretch(IMagickImage image, int targetWidth, int targetHeight)
{
image.Resize(new MagickGeometry(targetWidth, targetHeight) { IgnoreAspectRatio = true });
}
protected virtual void ResizeModeNone(IMagickImage image, int targetWidth, int targetHeight)
{
image.Resize(targetWidth, targetHeight);
}
}

@ -0,0 +1,8 @@
namespace Volo.Abp.Image;
public class MagickNetCompressOptions
{
public bool OptimalCompression { get; set; }
public bool IgnoreUnsupportedFormats { get; set; }
public bool Lossless { get; set; }
}

@ -1,78 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Volo.Abp.Content;
namespace Volo.Abp.Image;
//Rename: CompressImageAttribute
//Also introduce ResizeImageAttribute(...)
public class AbpImageCompressActionFilterAttribute : ActionFilterAttribute
{
public string[] Parameters { get; }
public AbpImageCompressActionFilterAttribute(params string[] parameters)
{
Parameters = parameters;
}
public async override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var parameters = Parameters.Any()
? context.ActionArguments.Where(x => Parameters.Contains(x.Key)).ToArray()
: context.ActionArguments.ToArray();
var parameterDescriptors = context.ActionDescriptor.Parameters.OfType<ControllerParameterDescriptor>();
var parameterMap = new Dictionary<string, ParameterInfo>();
foreach (var parameterDescriptor in parameterDescriptors)
{
var parameter = parameterDescriptor.ParameterInfo;
var attribute = parameter.GetCustomAttribute<AbpOriginalImageAttribute>();
if (attribute == null)
{
continue;
}
parameterMap.Add(attribute.Parameter, parameter);
}
foreach (var (key, value) in parameters)
{
object compressedValue = value switch {
IFormFile file => await file.CompressImageAsync(context.HttpContext.RequestServices),
IRemoteStreamContent remoteStreamContent => await remoteStreamContent.CompressImageAsync(context.HttpContext.RequestServices),
Stream stream => await stream.CompressImageAsync(context.HttpContext.RequestServices),
_ => null
};
if (parameterMap.TryGetValue(key, out var parameterInfo))
{
if (parameterInfo.Name != null)
{
context.ActionArguments.Add(parameterInfo.Name, value);
}
else if (value is IDisposable disposable)
{
disposable.Dispose();
}
}
else if (value is IDisposable disposable)
{
disposable.Dispose();
}
if (compressedValue != null)
{
context.ActionArguments[key] = compressedValue;
}
}
await next();
}
}

@ -1,9 +0,0 @@
using Volo.Abp.Modularity;
namespace Volo.Abp.Image;
[DependsOn(typeof(AbpImageModule))]
public class AbpImageWebModule : AbpModule
{
}

@ -1,14 +0,0 @@
using System;
namespace Volo.Abp.Image;
[AttributeUsage(AttributeTargets.Parameter)]
public class AbpOriginalImageAttribute : Attribute //TODO: Remove
{
public AbpOriginalImageAttribute(string parameter)
{
Parameter = parameter;
}
public string Parameter { get; }
}

@ -1,77 +0,0 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
namespace Volo.Abp.Image;
public static class IFormFileExtensions //TODO: Remove
{
public async static Task<IFormFile> CompressImageAsync(
this IFormFile formFile,
IImageFormat imageFormat,
IImageCompressor imageCompressor,
CancellationToken cancellationToken = default)
{
if (!formFile.ContentType.StartsWith("image"))
{
return formFile;
}
if (!imageCompressor.CanCompress(imageFormat))
{
return formFile;
}
var compressedImageStream = await formFile.OpenReadStream().CompressImageAsync(imageFormat, imageCompressor, cancellationToken);
var newFormFile = new FormFile(compressedImageStream, 0, compressedImageStream.Length, formFile.Name,
formFile.FileName) { Headers = formFile.Headers };
return newFormFile;
}
public async static Task<IFormFile> CompressImageAsync(this IFormFile formFile, IServiceProvider serviceProvider)
{
var imageFormatDetector = serviceProvider.GetRequiredService<IImageFormatDetector>();
var imageCompressorSelector = serviceProvider.GetRequiredService<IImageCompressorSelector>();
var format = imageFormatDetector.FindFormat(formFile.OpenReadStream());
return await formFile.CompressImageAsync(format, imageCompressorSelector.FindCompressor(format));
}
public static async Task<IFormFile> ResizeImageAsync(
this IFormFile formFile,
IImageResizeParameter imageResizeParameter,
IImageFormat imageFormat,
IImageResizer imageResizer,
CancellationToken cancellationToken = default)
{
if (!formFile.ContentType.StartsWith("image"))
{
return formFile;
}
if (!imageResizer.CanResize(imageFormat))
{
return formFile;
}
var resizedImageStream =
await formFile.OpenReadStream().ResizeImageAsync(imageResizeParameter, imageFormat, imageResizer, cancellationToken);
var newFormFile = new FormFile(resizedImageStream, 0, resizedImageStream.Length, formFile.Name,
formFile.FileName) { Headers = formFile.Headers };
return newFormFile;
}
public static Task<IFormFile> ResizeImageAsync(this IFormFile formFile,
IImageResizeParameter imageResizeParameter, IServiceProvider serviceProvider)
{
var imageFormatDetector = serviceProvider.GetRequiredService<IImageFormatDetector>();
var imageResizerSelector = serviceProvider.GetRequiredService<IImageResizerSelector>();
var format = imageFormatDetector.FindFormat(formFile.OpenReadStream());
return formFile.ResizeImageAsync(imageResizeParameter, format, imageResizerSelector.FindResizer(format));
}
}
Loading…
Cancel
Save