Add Volo.Abp.Image package

pull/16632/head
Salih 2 years ago
parent 286b371b01
commit 6cb4316a81

@ -439,6 +439,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Ldap.Abstractions"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Ddd.Domain.Shared", "src\Volo.Abp.Ddd.Domain.Shared\Volo.Abp.Ddd.Domain.Shared.csproj", "{0858571B-CE73-4AD6-BD06-EC9F0714D8E9}"
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", "src\Volo.Abp.Image\Volo.Abp.Image.csproj", "{3F9E50EF-09E4-4548-8E70-815ED946CEA4}"
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}"
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
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Image.MagickNet", "src\Volo.Abp.Image.MagickNet\Volo.Abp.Image.MagickNet.csproj", "{F701EDA5-D7EA-4AA7-9C57-83ED50CE72EC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -1309,6 +1319,26 @@ Global
{0858571B-CE73-4AD6-BD06-EC9F0714D8E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0858571B-CE73-4AD6-BD06-EC9F0714D8E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0858571B-CE73-4AD6-BD06-EC9F0714D8E9}.Release|Any CPU.Build.0 = Release|Any CPU
{32F3E84B-D02E-42BD-BC5C-0D211564EF30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{32F3E84B-D02E-42BD-BC5C-0D211564EF30}.Debug|Any CPU.Build.0 = Debug|Any CPU
{32F3E84B-D02E-42BD-BC5C-0D211564EF30}.Release|Any CPU.ActiveCfg = Release|Any CPU
{32F3E84B-D02E-42BD-BC5C-0D211564EF30}.Release|Any CPU.Build.0 = Release|Any CPU
{3F9E50EF-09E4-4548-8E70-815ED946CEA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3F9E50EF-09E4-4548-8E70-815ED946CEA4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3F9E50EF-09E4-4548-8E70-815ED946CEA4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3F9E50EF-09E4-4548-8E70-815ED946CEA4}.Release|Any CPU.Build.0 = Release|Any CPU
{78340A37-219E-4F2D-9AC6-40A7B467EEEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{78340A37-219E-4F2D-9AC6-40A7B467EEEC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{78340A37-219E-4F2D-9AC6-40A7B467EEEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{78340A37-219E-4F2D-9AC6-40A7B467EEEC}.Release|Any CPU.Build.0 = Release|Any CPU
{44467427-E0BE-492C-B9B4-82B362C183C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{44467427-E0BE-492C-B9B4-82B362C183C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{44467427-E0BE-492C-B9B4-82B362C183C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{44467427-E0BE-492C-B9B4-82B362C183C3}.Release|Any CPU.Build.0 = Release|Any CPU
{F701EDA5-D7EA-4AA7-9C57-83ED50CE72EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F701EDA5-D7EA-4AA7-9C57-83ED50CE72EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F701EDA5-D7EA-4AA7-9C57-83ED50CE72EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F701EDA5-D7EA-4AA7-9C57-83ED50CE72EC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1530,6 +1560,11 @@ Global
{8764DFAF-D13D-449A-9A5E-5D7F0B2D7FEF} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{0F80E95C-41E6-4F23-94FF-FC9D0B8D5D71} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{0858571B-CE73-4AD6-BD06-EC9F0714D8E9} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{3F9E50EF-09E4-4548-8E70-815ED946CEA4} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{32F3E84B-D02E-42BD-BC5C-0D211564EF30} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{44467427-E0BE-492C-B9B4-82B362C183C3} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{F701EDA5-D7EA-4AA7-9C57-83ED50CE72EC} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{78340A37-219E-4F2D-9AC6-40A7B467EEEC} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}

@ -7,28 +7,28 @@ public static class AbpStreamExtensions
{
public static byte[] GetAllBytes(this Stream stream)
{
using (var memoryStream = new MemoryStream())
if (stream is MemoryStream memoryStream)
{
if (stream.CanSeek)
{
stream.Position = 0;
}
stream.CopyTo(memoryStream);
return memoryStream.ToArray();
}
using (var ms = stream.CreateMemoryStream())
{
return ms.ToArray();
}
}
public static async Task<byte[]> GetAllBytesAsync(this Stream stream, CancellationToken cancellationToken = default)
{
using (var memoryStream = new MemoryStream())
if (stream is MemoryStream memoryStream)
{
if (stream.CanSeek)
{
stream.Position = 0;
}
await stream.CopyToAsync(memoryStream, cancellationToken);
return memoryStream.ToArray();
}
using (var ms = await stream.CreateMemoryStreamAsync(cancellationToken))
{
return ms.ToArray();
}
}
public static Task CopyToAsync(this Stream stream, Stream destination, CancellationToken cancellationToken)
@ -43,4 +43,36 @@ public static class AbpStreamExtensions
cancellationToken
);
}
public async static Task<MemoryStream> CreateMemoryStreamAsync(this Stream stream, CancellationToken cancellationToken = default)
{
if (stream.CanSeek)
{
stream.Position = 0;
}
var memoryStream = new MemoryStream();
await stream.CopyToAsync(memoryStream, cancellationToken);
if (stream.CanSeek)
{
stream.Position = 0;
}
memoryStream.Position = 0;
return memoryStream;
}
public static MemoryStream CreateMemoryStream(this Stream stream)
{
if (stream.CanSeek)
{
stream.Position = 0;
}
var memoryStream = new MemoryStream();
stream.CopyTo(memoryStream);
if (stream.CanSeek)
{
stream.Position = 0;
}
memoryStream.Position = 0;
return memoryStream;
}
}

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1;net7.0</TargetFrameworks>
<PackageId>Volo.Abp.Image.Abstractions</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,7 @@
using Volo.Abp.Modularity;
namespace Volo.Abp.Image;
public class AbpImageAbstractionsModule : AbpModule
{
}

@ -0,0 +1,14 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace Volo.Abp.Image;
public interface IImageCompressor
{
Task<Stream> CompressAsync(Stream stream, CancellationToken cancellationToken = default);
Stream Compress(Stream stream);
bool CanCompress(IImageFormat imageFormat);
}

@ -0,0 +1,6 @@
namespace Volo.Abp.Image;
public interface IImageCompressorSelector
{
IImageCompressor FindCompressor(IImageFormat imageFormat);
}

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

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

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

@ -0,0 +1,15 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace Volo.Abp.Image;
public interface IImageResizer
{
Task<Stream> ResizeAsync(Stream stream, IImageResizeParameter resizeParameter,
CancellationToken cancellationToken = default);
Stream Resize(Stream stream, IImageResizeParameter resizeParameter);
bool CanResize(IImageFormat imageFormat);
}

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

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

@ -0,0 +1,15 @@
namespace Volo.Abp.Image;
public enum ImageResizeMode
{
None,
Stretch,
BoxPad,
Min,
Max,
Crop,
Pad,
Fill,
Distort,
Default = Stretch
}

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFrameworks>netstandard2.0;</TargetFrameworks>
<PackageId>Volo.Abp.Image.ImageSharp</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Image.Abstractions\Volo.Abp.Image.Abstractions.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.4" />
</ItemGroup>
</Project>

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

@ -0,0 +1,87 @@
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 newStream = await stream.CreateMemoryStreamAsync(cancellationToken: cancellationToken);
using var image = await SixLabors.ImageSharp.Image.LoadAsync(newStream, cancellationToken);
newStream.Position = 0;
var format = await SixLabors.ImageSharp.Image.DetectFormatAsync(newStream, cancellationToken);
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}");
}
await image.SaveAsync(newStream, encoder, cancellationToken: cancellationToken);
newStream.SetLength(newStream.Position);
return newStream;
}
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)
{
return format?.MimeType switch {
"image/jpeg" => true,
"image/png" => true,
"image/webp" => true,
_ => false
};
}
}

@ -0,0 +1,13 @@
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.Name, format.DefaultMimeType);
}
}

@ -0,0 +1,100 @@
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,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1;net7.0</TargetFrameworks>
<PackageId>Volo.Abp.Image.MagicNet</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Image.Abstractions\Volo.Abp.Image.Abstractions.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Magick.NET-Q16-AnyCPU" Version="13.1.0" />
</ItemGroup>
</Project>

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

@ -0,0 +1,57 @@
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
{
_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,15 @@
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.Format.ToString(), format.MimeType ?? string.Empty);
}
}

@ -0,0 +1,177 @@
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
{
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,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<PackageId>Volo.Abp.Image.Web</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<IsPackable>true</IsPackable>
<OutputType>Library</OutputType>
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
<RootNamespace />
</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>
</ItemGroup>
</Project>

@ -0,0 +1,76 @@
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;
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();
}
}

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

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

@ -0,0 +1,77 @@
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
{
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));
}
}

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1;net7.0</TargetFrameworks>
<PackageId>Volo.Abp.Image</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" />
<ProjectReference Include="..\Volo.Abp.Image.Abstractions\Volo.Abp.Image.Abstractions.csproj" />
</ItemGroup>
</Project>

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

@ -0,0 +1,50 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Content;
namespace Volo.Abp.Image;
public static class IRemoteStreamContentExtensions
{
public async static Task<IRemoteStreamContent> CompressImageAsync(this IRemoteStreamContent remoteStreamContent, IImageFormat imageFormat, IImageCompressor imageCompressor, CancellationToken cancellationToken = default)
{
if (!imageCompressor.CanCompress(imageFormat))
{
return remoteStreamContent;
}
var compressedImageStream = await remoteStreamContent.GetStream().CompressImageAsync(imageFormat, imageCompressor, cancellationToken);
return new RemoteStreamContent(compressedImageStream, remoteStreamContent.FileName, remoteStreamContent.ContentType);
}
public async static Task<IRemoteStreamContent> CompressImageAsync(this IRemoteStreamContent remoteStreamContent, IServiceProvider serviceProvider, CancellationToken cancellationToken = default)
{
var imageFormatDetector = serviceProvider.GetRequiredService<IImageFormatDetector>();
var imageCompressorSelector = serviceProvider.GetRequiredService<IImageCompressorSelector>();
var format = imageFormatDetector.FindFormat(remoteStreamContent.GetStream());
return await remoteStreamContent.CompressImageAsync(format, imageCompressorSelector.FindCompressor(format), cancellationToken);
}
public async static Task<IRemoteStreamContent> ResizeImageAsync(this IRemoteStreamContent remoteStreamContent, IImageResizeParameter imageResizeParameter, IImageFormat imageFormat, IImageResizer imageResizer, CancellationToken cancellationToken = default)
{
if (!imageResizer.CanResize(imageFormat))
{
return remoteStreamContent;
}
var resizedImageStream = await remoteStreamContent.GetStream().ResizeImageAsync(imageResizeParameter, imageFormat, imageResizer, cancellationToken);
return new RemoteStreamContent(resizedImageStream, remoteStreamContent.FileName, remoteStreamContent.ContentType);
}
public async static Task<IRemoteStreamContent> ResizeImageAsync(this IRemoteStreamContent remoteStreamContent, IImageResizeParameter imageResizeParameter, IServiceProvider serviceProvider, CancellationToken cancellationToken = default)
{
var imageFormatDetector = serviceProvider.GetRequiredService<IImageFormatDetector>();
var imageResizerSelector = serviceProvider.GetRequiredService<IImageResizerSelector>();
var format = imageFormatDetector.FindFormat(remoteStreamContent.GetStream());
return await remoteStreamContent.ResizeImageAsync(imageResizeParameter, format, imageResizerSelector.FindResizer(format), cancellationToken);
}
}

@ -0,0 +1,20 @@
using System.Collections.Generic;
using System.Linq;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Image;
public class ImageCompressorSelector : IImageCompressorSelector, ITransientDependency
{
private readonly IEnumerable<IImageCompressor> _imageCompressors;
public ImageCompressorSelector(IEnumerable<IImageCompressor> imageCompressors)
{
_imageCompressors = imageCompressors;
}
public IImageCompressor FindCompressor(IImageFormat imageFormat)
{
return _imageCompressors.FirstOrDefault(x => x.CanCompress(imageFormat));
}
}

@ -0,0 +1,15 @@
namespace Volo.Abp.Image;
public class ImageResizeParameter : IImageResizeParameter
{
public ImageResizeParameter(int? width = null, int? height = null, ImageResizeMode? mode = null)
{
Mode = mode;
Width = width;
Height = height;
}
public int? Width { get; set; }
public int? Height { get; set; }
public ImageResizeMode? Mode { get; set; }
}

@ -0,0 +1,20 @@
using System.Collections.Generic;
using System.Linq;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Image;
public class ImageResizerSelector : IImageResizerSelector, ITransientDependency
{
private readonly IEnumerable<IImageResizer> _imageResizers;
public ImageResizerSelector(IEnumerable<IImageResizer> imageResizers)
{
_imageResizers = imageResizers;
}
public IImageResizer FindResizer(IImageFormat imageFormat)
{
return _imageResizers.FirstOrDefault(x => x.CanResize(imageFormat));
}
}

@ -0,0 +1,82 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
namespace Volo.Abp.Image;
public static class StreamExtensions
{
public static Stream CompressImage(this Stream stream, IImageFormat imageFormat, IImageCompressor imageCompressor)
{
if (!imageCompressor.CanCompress(imageFormat))
{
return stream;
}
return imageCompressor.Compress(stream);
}
public static Stream CompressImage(this Stream stream, IServiceProvider serviceProvider)
{
var imageFormatDetector = serviceProvider.GetRequiredService<IImageFormatDetector>();
var imageCompressorSelector = serviceProvider.GetRequiredService<IImageCompressorSelector>();
var format = imageFormatDetector.FindFormat(stream);
return stream.CompressImage(format, imageCompressorSelector.FindCompressor(format));
}
public static Task<Stream> CompressImageAsync(this Stream stream, IImageFormat imageFormat, IImageCompressor imageCompressor, CancellationToken cancellationToken = default)
{
if (!imageCompressor.CanCompress(imageFormat))
{
return Task.FromResult(stream);
}
return imageCompressor.CompressAsync(stream, cancellationToken);
}
public static Task<Stream> CompressImageAsync(this Stream stream, IServiceProvider serviceProvider, CancellationToken cancellationToken = default)
{
var imageFormatDetector = serviceProvider.GetRequiredService<IImageFormatDetector>();
var imageCompressorSelector = serviceProvider.GetRequiredService<IImageCompressorSelector>();
var format = imageFormatDetector.FindFormat(stream);
return stream.CompressImageAsync(format, imageCompressorSelector.FindCompressor(format), cancellationToken);
}
public static Stream ResizeImage(this Stream stream, IImageResizeParameter imageResizeParameter, IImageFormat imageFormat, IImageResizer imageResizer)
{
if (!imageResizer.CanResize(imageFormat))
{
return stream;
}
return imageResizer.Resize(stream, imageResizeParameter);
}
public static Stream ResizeImage(this Stream stream, IImageResizeParameter imageResizeParameter, IServiceProvider serviceProvider)
{
var imageFormatDetector = serviceProvider.GetRequiredService<IImageFormatDetector>();
var imageResizerSelector = serviceProvider.GetRequiredService<IImageResizerSelector>();
var format = imageFormatDetector.FindFormat(stream);
return stream.ResizeImage(imageResizeParameter, format, imageResizerSelector.FindResizer(format));
}
public static Task<Stream> ResizeImageAsync(this Stream stream, IImageResizeParameter imageResizeParameter, IImageFormat imageFormat, IImageResizer imageResizer, CancellationToken cancellationToken = default)
{
if (!imageResizer.CanResize(imageFormat))
{
return Task.FromResult(stream);
}
return imageResizer.ResizeAsync(stream, imageResizeParameter, cancellationToken);
}
public static Task<Stream> ResizeImageAsync(this Stream stream, IImageResizeParameter imageResizeParameter, IServiceProvider serviceProvider, CancellationToken cancellationToken = default)
{
var imageFormatDetector = serviceProvider.GetRequiredService<IImageFormatDetector>();
var imageResizerSelector = serviceProvider.GetRequiredService<IImageResizerSelector>();
var format = imageFormatDetector.FindFormat(stream);
return stream.ResizeImageAsync(imageResizeParameter, format, imageResizerSelector.FindResizer(format), cancellationToken);
}
}
Loading…
Cancel
Save