diff --git a/Directory.Packages.props b/Directory.Packages.props
index 67c100c20b..a6d8cfdc4f 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -140,6 +140,9 @@
+
+
+
diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln
index cd003b7f07..42339a60ac 100644
--- a/framework/Volo.Abp.sln
+++ b/framework/Volo.Abp.sln
@@ -459,6 +459,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Imaging.AspNetCore
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Maui.Client", "src\Volo.Abp.Maui.Client\Volo.Abp.Maui.Client.csproj", "{F19A6E0C-F719-4ED9-A024-14E4B8D40883}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Imaging.SkiaSharp", "src\Volo.Abp.Imaging.SkiaSharp\Volo.Abp.Imaging.SkiaSharp.csproj", "{198683D0-7DC6-40F2-B81B-8E446E70A9DE}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Imaging.SkiaSharp.Tests", "test\Volo.Abp.Imaging.SkiaSharp.Tests\Volo.Abp.Imaging.SkiaSharp.Tests.csproj", "{DFAF8763-D1D6-4EB4-B459-20E31007FE2F}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -1369,6 +1373,14 @@ Global
{F19A6E0C-F719-4ED9-A024-14E4B8D40883}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F19A6E0C-F719-4ED9-A024-14E4B8D40883}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F19A6E0C-F719-4ED9-A024-14E4B8D40883}.Release|Any CPU.Build.0 = Release|Any CPU
+ {198683D0-7DC6-40F2-B81B-8E446E70A9DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {198683D0-7DC6-40F2-B81B-8E446E70A9DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {198683D0-7DC6-40F2-B81B-8E446E70A9DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {198683D0-7DC6-40F2-B81B-8E446E70A9DE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DFAF8763-D1D6-4EB4-B459-20E31007FE2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DFAF8763-D1D6-4EB4-B459-20E31007FE2F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DFAF8763-D1D6-4EB4-B459-20E31007FE2F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DFAF8763-D1D6-4EB4-B459-20E31007FE2F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1600,6 +1612,8 @@ Global
{62B2B8C9-8F24-4D31-894F-C1F0728D32AB} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{983B0136-384B-4439-B374-31111FFAA286} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{F19A6E0C-F719-4ED9-A024-14E4B8D40883} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
+ {198683D0-7DC6-40F2-B81B-8E446E70A9DE} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
+ {DFAF8763-D1D6-4EB4-B459-20E31007FE2F} = {447C8A77-E5F0-4538-8687-7383196D04EA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}
diff --git a/framework/src/Volo.Abp.Imaging.SkiaSharp/FodyWeavers.xml b/framework/src/Volo.Abp.Imaging.SkiaSharp/FodyWeavers.xml
new file mode 100644
index 0000000000..1715698ccd
--- /dev/null
+++ b/framework/src/Volo.Abp.Imaging.SkiaSharp/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Imaging.SkiaSharp/FodyWeavers.xsd b/framework/src/Volo.Abp.Imaging.SkiaSharp/FodyWeavers.xsd
new file mode 100644
index 0000000000..3f3946e282
--- /dev/null
+++ b/framework/src/Volo.Abp.Imaging.SkiaSharp/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Imaging.SkiaSharp/Volo.Abp.Imaging.SkiaSharp.csproj b/framework/src/Volo.Abp.Imaging.SkiaSharp/Volo.Abp.Imaging.SkiaSharp.csproj
new file mode 100644
index 0000000000..857cb0f92f
--- /dev/null
+++ b/framework/src/Volo.Abp.Imaging.SkiaSharp/Volo.Abp.Imaging.SkiaSharp.csproj
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+ netstandard2.0;netstandard2.1;net8.0
+ enable
+ Nullable
+ Volo.Abp.Imaging.SkiaSharp
+ $(AssetTargetFallback);portable-net45+win8+wp8+wpa81;
+ false
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/framework/src/Volo.Abp.Imaging.SkiaSharp/Volo/Abp/Imaging/AbpImagingSkiaSharpModule.cs b/framework/src/Volo.Abp.Imaging.SkiaSharp/Volo/Abp/Imaging/AbpImagingSkiaSharpModule.cs
new file mode 100644
index 0000000000..79fc4314aa
--- /dev/null
+++ b/framework/src/Volo.Abp.Imaging.SkiaSharp/Volo/Abp/Imaging/AbpImagingSkiaSharpModule.cs
@@ -0,0 +1,8 @@
+using Volo.Abp.Modularity;
+
+namespace Volo.Abp.Imaging;
+
+[DependsOn(typeof(AbpImagingAbstractionsModule))]
+public class AbpImagingSkiaSharpModule : AbpModule
+{
+}
diff --git a/framework/src/Volo.Abp.Imaging.SkiaSharp/Volo/Abp/Imaging/SkiaSharpImageResizerContributor.cs b/framework/src/Volo.Abp.Imaging.SkiaSharp/Volo/Abp/Imaging/SkiaSharpImageResizerContributor.cs
new file mode 100644
index 0000000000..760acd0916
--- /dev/null
+++ b/framework/src/Volo.Abp.Imaging.SkiaSharp/Volo/Abp/Imaging/SkiaSharpImageResizerContributor.cs
@@ -0,0 +1,98 @@
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Options;
+using SkiaSharp;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Http;
+
+namespace Volo.Abp.Imaging;
+
+public class SkiaSharpImageResizerContributor : IImageResizerContributor, ITransientDependency
+{
+ protected SkiaSharpResizerOptions Options { get; }
+
+ public SkiaSharpImageResizerContributor(IOptions options)
+ {
+ Options = options.Value;
+ }
+
+ public virtual async Task> TryResizeAsync(byte[] bytes, ImageResizeArgs resizeArgs, string? mimeType = null, CancellationToken cancellationToken = default)
+ {
+ if (!mimeType.IsNullOrWhiteSpace() && !CanResize(mimeType))
+ {
+ return new ImageResizeResult(bytes, ImageProcessState.Unsupported);
+ }
+
+ using (var memoryStream = new MemoryStream(bytes))
+ {
+ var result = await TryResizeAsync(memoryStream, resizeArgs, mimeType, cancellationToken);
+
+ if (result.State != ImageProcessState.Done)
+ {
+ return new ImageResizeResult(bytes, result.State);
+ }
+
+ var newBytes = await result.Result.GetAllBytesAsync(cancellationToken);
+
+ result.Result.Dispose();
+
+ return new ImageResizeResult(newBytes, result.State);
+ }
+ }
+
+ public virtual async Task> TryResizeAsync(Stream stream, ImageResizeArgs resizeArgs, string? mimeType = null, CancellationToken cancellationToken = default)
+ {
+ if (!mimeType.IsNullOrWhiteSpace() && !CanResize(mimeType))
+ {
+ return new ImageResizeResult(stream, ImageProcessState.Unsupported);
+ }
+
+ var (memoryBitmapStream, memorySkCodecStream) = await CreateMemoryStream(stream);
+
+ using (var original = SKBitmap.Decode(memoryBitmapStream))
+ {
+ using (var resized = original.Resize(new SKImageInfo(resizeArgs.Width, resizeArgs.Height), Options.SKFilterQuality))
+ {
+ using (var image = SKImage.FromBitmap(resized))
+ {
+ using (var codec = SKCodec.Create(memorySkCodecStream))
+ {
+ var memoryStream = new MemoryStream();
+ image.Encode(codec.EncodedFormat, Options.Quality).SaveTo(memoryStream);
+ return new ImageResizeResult(memoryStream, ImageProcessState.Done);
+ }
+ }
+ }
+ }
+ }
+
+ protected virtual async Task<(MemoryStream, MemoryStream)> CreateMemoryStream(Stream stream)
+ {
+ var streamPosition = stream.Position;
+
+ var memoryBitmapStream = new MemoryStream();
+ var memorySkCodecStream = new MemoryStream();
+
+ await stream.CopyToAsync(memoryBitmapStream);
+ stream.Position = streamPosition;
+ await stream.CopyToAsync(memorySkCodecStream);
+ stream.Position = streamPosition;
+
+ memoryBitmapStream.Position = 0;
+ memorySkCodecStream.Position = 0;
+
+ return (memoryBitmapStream, memorySkCodecStream);
+ }
+
+ protected virtual bool CanResize(string? mimeType)
+ {
+ return mimeType switch {
+ MimeTypes.Image.Jpeg => true,
+ MimeTypes.Image.Png => true,
+ MimeTypes.Image.Webp => true,
+ _ => false
+ };
+ }
+}
diff --git a/framework/src/Volo.Abp.Imaging.SkiaSharp/Volo/Abp/Imaging/SkiaSharpResizerOptions.cs b/framework/src/Volo.Abp.Imaging.SkiaSharp/Volo/Abp/Imaging/SkiaSharpResizerOptions.cs
new file mode 100644
index 0000000000..6bc220feb1
--- /dev/null
+++ b/framework/src/Volo.Abp.Imaging.SkiaSharp/Volo/Abp/Imaging/SkiaSharpResizerOptions.cs
@@ -0,0 +1,16 @@
+using SkiaSharp;
+
+namespace Volo.Abp.Imaging;
+
+public class SkiaSharpResizerOptions
+{
+ public SKFilterQuality SKFilterQuality { get; set; }
+
+ public int Quality { get; set; }
+
+ public SkiaSharpResizerOptions()
+ {
+ SKFilterQuality = SKFilterQuality.None;
+ Quality = 75;
+ }
+}
diff --git a/framework/test/Volo.Abp.Imaging.SkiaSharp.Tests/Volo.Abp.Imaging.SkiaSharp.Tests.csproj b/framework/test/Volo.Abp.Imaging.SkiaSharp.Tests/Volo.Abp.Imaging.SkiaSharp.Tests.csproj
new file mode 100644
index 0000000000..3272fc325a
--- /dev/null
+++ b/framework/test/Volo.Abp.Imaging.SkiaSharp.Tests/Volo.Abp.Imaging.SkiaSharp.Tests.csproj
@@ -0,0 +1,19 @@
+
+
+
+
+
+ net8.0
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/framework/test/Volo.Abp.Imaging.SkiaSharp.Tests/Volo/Abp/Imaging/AbpImagingMagickNetTestModule.cs b/framework/test/Volo.Abp.Imaging.SkiaSharp.Tests/Volo/Abp/Imaging/AbpImagingMagickNetTestModule.cs
new file mode 100644
index 0000000000..7fce00d3ac
--- /dev/null
+++ b/framework/test/Volo.Abp.Imaging.SkiaSharp.Tests/Volo/Abp/Imaging/AbpImagingMagickNetTestModule.cs
@@ -0,0 +1,14 @@
+using Volo.Abp.Autofac;
+using Volo.Abp.Modularity;
+
+namespace Volo.Abp.Imaging;
+
+[DependsOn(
+ typeof(AbpAutofacModule),
+ typeof(AbpImagingSkiaSharpModule),
+ typeof(AbpTestBaseModule)
+)]
+public class AbpImagingSkiaSharpTestModule : AbpModule
+{
+
+}
diff --git a/framework/test/Volo.Abp.Imaging.SkiaSharp.Tests/Volo/Abp/Imaging/AbpImagingSkiaSharpTestModule.cs b/framework/test/Volo.Abp.Imaging.SkiaSharp.Tests/Volo/Abp/Imaging/AbpImagingSkiaSharpTestModule.cs
new file mode 100644
index 0000000000..953e8c4b3a
--- /dev/null
+++ b/framework/test/Volo.Abp.Imaging.SkiaSharp.Tests/Volo/Abp/Imaging/AbpImagingSkiaSharpTestModule.cs
@@ -0,0 +1,11 @@
+using Volo.Abp.Testing;
+
+namespace Volo.Abp.Imaging;
+
+public abstract class AbpImagingSkiaSharpTestBase : AbpIntegratedTest
+{
+ protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
+ {
+ options.UseAutofac();
+ }
+}
diff --git a/framework/test/Volo.Abp.Imaging.SkiaSharp.Tests/Volo/Abp/Imaging/SkiaSharpImageResizerTests.cs b/framework/test/Volo.Abp.Imaging.SkiaSharp.Tests/Volo/Abp/Imaging/SkiaSharpImageResizerTests.cs
new file mode 100644
index 0000000000..460c83bf5b
--- /dev/null
+++ b/framework/test/Volo.Abp.Imaging.SkiaSharp.Tests/Volo/Abp/Imaging/SkiaSharpImageResizerTests.cs
@@ -0,0 +1,75 @@
+using System.IO;
+using System.Threading.Tasks;
+using Shouldly;
+using Xunit;
+
+namespace Volo.Abp.Imaging;
+
+public class SkiaSharpImageResizerTests : AbpImagingSkiaSharpTestBase
+{
+ public IImageResizer ImageResizer { get; }
+
+ public SkiaSharpImageResizerTests()
+ {
+ ImageResizer = GetRequiredService();
+ }
+
+ [Fact]
+ public async Task Should_Resize_Jpg()
+ {
+ await using var jpegImage = ImageFileHelper.GetJpgTestFileStream();
+ var resizedImage = await ImageResizer.ResizeAsync(jpegImage, new ImageResizeArgs(100, 100));
+
+ resizedImage.ShouldNotBeNull();
+ resizedImage.State.ShouldBe(ImageProcessState.Done);
+ resizedImage.Result.Length.ShouldBeLessThan(jpegImage.Length);
+
+ resizedImage.Result.Dispose();
+ }
+
+ [Fact]
+ public async Task Should_Resize_Png()
+ {
+ await using var pngImage = ImageFileHelper.GetPngTestFileStream();
+ var resizedImage = await ImageResizer.ResizeAsync(pngImage, new ImageResizeArgs(100, 100));
+
+ resizedImage.ShouldNotBeNull();
+ resizedImage.State.ShouldBe(ImageProcessState.Done);
+ resizedImage.Result.Length.ShouldBeLessThan(pngImage.Length);
+
+ resizedImage.Result.Dispose();
+ }
+
+ [Fact]
+ public async Task Should_Resize_Webp()
+ {
+ await using var webpImage = ImageFileHelper.GetWebpTestFileStream();
+ var resizedImage = await ImageResizer.ResizeAsync(webpImage, new ImageResizeArgs(100, 100));
+
+ resizedImage.ShouldNotBeNull();
+ resizedImage.State.ShouldBe(ImageProcessState.Done);
+ resizedImage.Result.Length.ShouldBeLessThan(webpImage.Length);
+
+ resizedImage.Result.Dispose();
+ }
+
+ [Fact]
+ public async Task Should_Resize_Stream_And_Byte_Array_The_Same()
+ {
+ await using var jpegImage = ImageFileHelper.GetJpgTestFileStream();
+ var resizedImage1 = await ImageResizer.ResizeAsync(jpegImage, new ImageResizeArgs(100, 100));
+ var resizedImage2 = await ImageResizer.ResizeAsync(await jpegImage.GetAllBytesAsync(), new ImageResizeArgs(100, 100));
+
+ resizedImage1.ShouldNotBeNull();
+ resizedImage1.State.ShouldBe(ImageProcessState.Done);
+ resizedImage1.Result.Length.ShouldBeLessThan(jpegImage.Length);
+
+ resizedImage2.ShouldNotBeNull();
+ resizedImage2.State.ShouldBe(ImageProcessState.Done);
+ resizedImage2.Result.LongLength.ShouldBeLessThan(jpegImage.Length);
+
+ resizedImage1.Result.Length.ShouldBe(resizedImage2.Result.LongLength);
+
+ resizedImage1.Result.Dispose();
+ }
+}
diff --git a/nupkg/common.ps1 b/nupkg/common.ps1
index 96d61dc74d..78de2aef44 100644
--- a/nupkg/common.ps1
+++ b/nupkg/common.ps1
@@ -204,6 +204,7 @@ $projects = (
"framework/src/Volo.Abp.Imaging.AspNetCore",
"framework/src/Volo.Abp.Imaging.ImageSharp",
"framework/src/Volo.Abp.Imaging.MagickNet",
+ "framework/src/Volo.Abp.Imaging.SkiaSharp",
"framework/src/Volo.Abp.Json",
"framework/src/Volo.Abp.Json.Abstractions",
"framework/src/Volo.Abp.Json.Newtonsoft",