Browse Source

反病毒扫描

懒得勤快 2 years ago
parent
commit
23c5292c78

+ 2 - 8
Masuit.Tools.Abstractions/Masuit.Tools.Abstractions.csproj

@@ -3,7 +3,7 @@
         <TargetFrameworks>netstandard2.0;netstandard2.1;net461;net5;net6;net7</TargetFrameworks>
         <LangVersion>latest</LangVersion>
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-        <Version>2.6.4.1</Version>
+        <Version>2.6.5</Version>
         <Authors>懒得勤快</Authors>
         <Description>新手友好的C#万能工具库,码数吐司库,Masuit.Tools基础公共库(适用于.NET4.6.1/.NET Standard2.0及以上项目),包含一些常用的操作类,大都是静态类,加密解密,反射操作,Excel简单导出,权重随机筛选算法,分布式短id,表达式树,linq扩展,文件压缩,多线程下载和FTP客户端,硬件信息,字符串扩展方法,日期时间扩展操作,中国农历,大文件拷贝,图像裁剪,验证码,断点续传,集合扩展等常用封装。
             官网教程:https://tools.masuit.org
@@ -52,7 +52,7 @@
         <PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
         <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
         <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
-        <PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta15" />
+        <PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0" />
         <PackageReference Include="System.Configuration.ConfigurationManager" Version="7.0.0" />
         <PackageReference Include="System.Management" Version="7.0.2" />
         <PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.7.0" />
@@ -60,35 +60,29 @@
     </ItemGroup>
 
     <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0'">
-        <PackageReference Include="SixLabors.ImageSharp" Version="[2.1.3]" />
         <PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
         <PackageReference Include="System.Diagnostics.PerformanceCounter" Version="7.0.0" />
     </ItemGroup>
 
     <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.1'">
-        <PackageReference Include="SixLabors.ImageSharp" Version="[2.1.3]" />
         <PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
         <PackageReference Include="System.Diagnostics.PerformanceCounter" Version="7.0.0" />
     </ItemGroup>
 
     <ItemGroup Condition=" '$(TargetFramework)' == 'net5'">
-        <PackageReference Include="SixLabors.ImageSharp" Version="[2.1.3]" />
         <PackageReference Include="System.Diagnostics.PerformanceCounter" Version="7.0.0" />
     </ItemGroup>
 
     <ItemGroup Condition=" '$(TargetFramework)' == 'net6'">
-        <PackageReference Include="SixLabors.ImageSharp" Version="3.0.1" />
         <PackageReference Include="System.Diagnostics.PerformanceCounter" Version="7.0.0" />
     </ItemGroup>
 
     <ItemGroup Condition=" '$(TargetFramework)' == 'net7'">
-        <PackageReference Include="SixLabors.ImageSharp" Version="3.0.1" />
         <PackageReference Include="System.Diagnostics.PerformanceCounter" Version="7.0.0" />
     </ItemGroup>
 
     <ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
         <Reference Include="System.Web" />
-        <PackageReference Include="SixLabors.ImageSharp" Version="[2.1.3]" />
         <PackageReference Include="System.Buffers" version="[4.5.1]" targetFramework="net461" />
         <PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
         <PackageReference Include="System.Diagnostics.PerformanceCounter" Version="[4.5]" />

+ 204 - 204
Masuit.Tools.Abstractions/Media/ImageWatermarker.cs

@@ -10,208 +10,208 @@ using SixLabors.ImageSharp.Formats;
 
 namespace Masuit.Tools.Media
 {
-    public class ImageWatermarker
-    {
-        /// <summary>
-        /// 是否跳过小缩略图
-        /// </summary>
-        public bool SkipWatermarkForSmallImages { get; set; }
-
-        /// <summary>
-        /// 小图像素大小
-        /// </summary>
-        public int SmallImagePixelsThreshold { get; set; }
-
-        public IImageEncoder ImageEncoder { get; set; }
-
-        private readonly Stream _stream;
-
-        /// <summary>
-        ///
-        /// </summary>
-        /// <param name="originStream">图片流</param>
-        public ImageWatermarker(Stream originStream)
-        {
-            _stream = originStream;
-        }
-
-        /// <summary>
-        ///
-        /// </summary>
-        /// <param name="originStream">图片流</param>
-        /// <param name="encoder">指定编码器</param>
-        public ImageWatermarker(Stream originStream, IImageEncoder encoder) : this(originStream)
-        {
-            ImageEncoder = encoder;
-        }
-
-        /// <summary>
-        ///
-        /// </summary>
-        /// <param name="originStream">图片流</param>
-        /// <param name="encoder">指定编码器</param>
-        /// <param name="skipWatermarkForSmallImages">是否跳过小缩略图</param>
-        /// <param name="smallImagePixelsThreshold">小图像素大小</param>
-        public ImageWatermarker(Stream originStream, IImageEncoder encoder, bool skipWatermarkForSmallImages, int smallImagePixelsThreshold) : this(originStream, encoder)
-        {
-            SkipWatermarkForSmallImages = skipWatermarkForSmallImages;
-            SmallImagePixelsThreshold = smallImagePixelsThreshold;
-        }
-
-        /// <summary>
-        /// 添加水印
-        /// </summary>
-        /// <param name="watermarkText">水印文字</param>
-        /// <param name="ttfFontPath">ttf字体文件路径</param>
-        /// <param name="fontSize">字体大小</param>
-        /// <param name="color">字体颜色</param>
-        /// <param name="watermarkPosition">水印位置</param>
-        /// <param name="textPadding">水印边距</param>
-        /// <returns></returns>
-        public PooledMemoryStream AddWatermark(string watermarkText, string ttfFontPath, int fontSize, Color color, WatermarkPosition watermarkPosition = WatermarkPosition.BottomRight, int textPadding = 10)
-        {
-            var fonts = new FontCollection();
-            var fontFamily = fonts.Add(ttfFontPath); //字体的路径(电脑自带字体库,去copy出来)
-            var font = new Font(fontFamily, fontSize, FontStyle.Bold);
-            return AddWatermark(watermarkText, font, color, watermarkPosition, textPadding);
-        }
-
-        /// <summary>
-        /// 添加水印
-        /// </summary>
-        /// <param name="watermarkText">水印文字</param>
-        /// <param name="color">水印颜色</param>
-        /// <param name="watermarkPosition">水印位置</param>
-        /// <param name="textPadding">边距</param>
-        /// <param name="font">字体</param>
-        public PooledMemoryStream AddWatermark(string watermarkText, Font font, Color color, WatermarkPosition watermarkPosition = WatermarkPosition.BottomRight, int textPadding = 10)
-        {
-            var imageFormat = Image.DetectFormat(_stream);
-            _stream.Seek(0, SeekOrigin.Begin);
-            using var img = Image.Load(_stream);
-            var textMeasure = TextMeasurer.Measure(watermarkText, new TextOptions(font));
-            if (SkipWatermarkForSmallImages && (img.Height < Math.Sqrt(SmallImagePixelsThreshold) || img.Width < Math.Sqrt(SmallImagePixelsThreshold) || img.Width <= textMeasure.Width))
-            {
-                return _stream as PooledMemoryStream ?? _stream.SaveAsMemoryStream();
-            }
-
-            if (img.Width / font.Size > 50)
-            {
-                font = font.Family.CreateFont(img.Width * 1f / 50);
-            }
-
-            float x, y;
-            textPadding += (img.Width - 1000) / 100;
-            switch (watermarkPosition)
-            {
-                case WatermarkPosition.TopRight:
-                    x = img.Width - textMeasure.Width - textPadding;
-                    y = textPadding;
-                    break;
-
-                case WatermarkPosition.BottomLeft:
-                    x = textPadding;
-                    y = img.Height - textMeasure.Height - textPadding;
-                    break;
-
-                case WatermarkPosition.BottomRight:
-                    x = img.Width - textMeasure.Width - textPadding;
-                    y = img.Height - textMeasure.Height - textPadding;
-                    break;
-
-                case WatermarkPosition.Center:
-                    x = (img.Width - textMeasure.Width) / 2;
-                    y = (img.Height - textMeasure.Height) / 2;
-                    break;
-
-                default:
-                    x = textPadding;
-                    y = textPadding;
-                    break;
-            }
-
-            img.Mutate(c => c.DrawText(watermarkText, font, color, new PointF(x, y)));
-            var ms = new PooledMemoryStream();
-            if (ImageEncoder == null)
-            {
-                img.Save(ms, imageFormat);
-            }
-            else
-            {
-                img.Save(ms, ImageEncoder);
-            }
-            ms.Position = 0;
-            return ms;
-        }
-
-        /// <summary>
-        /// 添加水印
-        /// </summary>
-        /// <param name="watermarkImage">水印图片</param>
-        /// <param name="opacity">水印图片</param>
-        /// <param name="watermarkPosition">水印位置</param>
-        /// <param name="padding">水印边距</param>
-        /// <returns></returns>
-        public PooledMemoryStream AddWatermark(Stream watermarkImage, float opacity = 1f, WatermarkPosition watermarkPosition = WatermarkPosition.BottomRight, int padding = 20)
-        {
-            using var img = Image.Load(_stream);
-            var height = img.Height;
-            var width = img.Width;
-            if (SkipWatermarkForSmallImages && (height < Math.Sqrt(SmallImagePixelsThreshold) || width < Math.Sqrt(SmallImagePixelsThreshold)))
-            {
-                return _stream as PooledMemoryStream ?? _stream.SaveAsMemoryStream();
-            }
-
-            var watermark = Image.Load(watermarkImage);
-            watermark.Mutate(c => c.Resize(new ResizeOptions()
-            {
-                Size = new Size
-                {
-                    Width = width / 10,
-                    Height = height / 10,
-                },
-                Mode = ResizeMode.Pad,
-                Sampler = new BicubicResampler()
-            }));
-            int x, y;
-            padding += (width - 1000) / 100;
-            switch (watermarkPosition)
-            {
-                case WatermarkPosition.TopRight:
-                    x = width - watermark.Width - padding;
-                    y = padding;
-                    break;
-
-                case WatermarkPosition.BottomLeft:
-                    x = padding;
-                    y = height - watermark.Height - padding;
-                    break;
-
-                case WatermarkPosition.BottomRight:
-                    x = width - watermark.Width - padding;
-                    y = height - watermark.Height - padding;
-                    break;
-
-                case WatermarkPosition.Center:
-                    x = (img.Width - watermark.Width) / 2;
-                    y = (img.Height - watermark.Height) / 2;
-                    break;
-
-                default:
-                    x = padding;
-                    y = padding;
-                    break;
-            }
-
-            img.Mutate(c =>
-            {
-                c.DrawImage(watermark, new Point(x, y), opacity);
-                watermark.Dispose();
-            });
-            var ms = new PooledMemoryStream();
-            img.SaveAsWebp(ms);
-            ms.Position = 0;
-            return ms;
-        }
-    }
+	public class ImageWatermarker
+	{
+		/// <summary>
+		/// 是否跳过小缩略图
+		/// </summary>
+		public bool SkipWatermarkForSmallImages { get; set; }
+
+		/// <summary>
+		/// 小图像素大小
+		/// </summary>
+		public int SmallImagePixelsThreshold { get; set; }
+
+		public IImageEncoder ImageEncoder { get; set; }
+
+		private readonly Stream _stream;
+
+		/// <summary>
+		///
+		/// </summary>
+		/// <param name="originStream">图片流</param>
+		public ImageWatermarker(Stream originStream)
+		{
+			_stream = originStream;
+		}
+
+		/// <summary>
+		///
+		/// </summary>
+		/// <param name="originStream">图片流</param>
+		/// <param name="encoder">指定编码器</param>
+		public ImageWatermarker(Stream originStream, IImageEncoder encoder) : this(originStream)
+		{
+			ImageEncoder = encoder;
+		}
+
+		/// <summary>
+		///
+		/// </summary>
+		/// <param name="originStream">图片流</param>
+		/// <param name="encoder">指定编码器</param>
+		/// <param name="skipWatermarkForSmallImages">是否跳过小缩略图</param>
+		/// <param name="smallImagePixelsThreshold">小图像素大小</param>
+		public ImageWatermarker(Stream originStream, IImageEncoder encoder, bool skipWatermarkForSmallImages, int smallImagePixelsThreshold) : this(originStream, encoder)
+		{
+			SkipWatermarkForSmallImages = skipWatermarkForSmallImages;
+			SmallImagePixelsThreshold = smallImagePixelsThreshold;
+		}
+
+		/// <summary>
+		/// 添加水印
+		/// </summary>
+		/// <param name="watermarkText">水印文字</param>
+		/// <param name="ttfFontPath">ttf字体文件路径</param>
+		/// <param name="fontSize">字体大小</param>
+		/// <param name="color">字体颜色</param>
+		/// <param name="watermarkPosition">水印位置</param>
+		/// <param name="textPadding">水印边距</param>
+		/// <returns></returns>
+		public PooledMemoryStream AddWatermark(string watermarkText, string ttfFontPath, int fontSize, Color color, WatermarkPosition watermarkPosition = WatermarkPosition.BottomRight, int textPadding = 10)
+		{
+			var fonts = new FontCollection();
+			var fontFamily = fonts.Add(ttfFontPath); //字体的路径(电脑自带字体库,去copy出来)
+			var font = new Font(fontFamily, fontSize, FontStyle.Bold);
+			return AddWatermark(watermarkText, font, color, watermarkPosition, textPadding);
+		}
+
+		/// <summary>
+		/// 添加水印
+		/// </summary>
+		/// <param name="watermarkText">水印文字</param>
+		/// <param name="color">水印颜色</param>
+		/// <param name="watermarkPosition">水印位置</param>
+		/// <param name="textPadding">边距</param>
+		/// <param name="font">字体</param>
+		public PooledMemoryStream AddWatermark(string watermarkText, Font font, Color color, WatermarkPosition watermarkPosition = WatermarkPosition.BottomRight, int textPadding = 10)
+		{
+			var imageFormat = Image.DetectFormat(_stream);
+			_stream.Seek(0, SeekOrigin.Begin);
+			using var img = Image.Load(_stream);
+			var textMeasure = TextMeasurer.MeasureSize(watermarkText, new TextOptions(font));
+			if (SkipWatermarkForSmallImages && (img.Height < Math.Sqrt(SmallImagePixelsThreshold) || img.Width < Math.Sqrt(SmallImagePixelsThreshold) || img.Width <= textMeasure.Width))
+			{
+				return _stream as PooledMemoryStream ?? _stream.SaveAsMemoryStream();
+			}
+
+			if (img.Width / font.Size > 50)
+			{
+				font = font.Family.CreateFont(img.Width * 1f / 50);
+			}
+
+			float x, y;
+			textPadding += (img.Width - 1000) / 100;
+			switch (watermarkPosition)
+			{
+				case WatermarkPosition.TopRight:
+					x = img.Width - textMeasure.Width - textPadding;
+					y = textPadding;
+					break;
+
+				case WatermarkPosition.BottomLeft:
+					x = textPadding;
+					y = img.Height - textMeasure.Height - textPadding;
+					break;
+
+				case WatermarkPosition.BottomRight:
+					x = img.Width - textMeasure.Width - textPadding;
+					y = img.Height - textMeasure.Height - textPadding;
+					break;
+
+				case WatermarkPosition.Center:
+					x = (img.Width - textMeasure.Width) / 2;
+					y = (img.Height - textMeasure.Height) / 2;
+					break;
+
+				default:
+					x = textPadding;
+					y = textPadding;
+					break;
+			}
+
+			img.Mutate(c => c.DrawText(watermarkText, font, color, new PointF(x, y)));
+			var ms = new PooledMemoryStream();
+			if (ImageEncoder == null)
+			{
+				img.Save(ms, imageFormat);
+			}
+			else
+			{
+				img.Save(ms, ImageEncoder);
+			}
+			ms.Position = 0;
+			return ms;
+		}
+
+		/// <summary>
+		/// 添加水印
+		/// </summary>
+		/// <param name="watermarkImage">水印图片</param>
+		/// <param name="opacity">水印图片</param>
+		/// <param name="watermarkPosition">水印位置</param>
+		/// <param name="padding">水印边距</param>
+		/// <returns></returns>
+		public PooledMemoryStream AddWatermark(Stream watermarkImage, float opacity = 1f, WatermarkPosition watermarkPosition = WatermarkPosition.BottomRight, int padding = 20)
+		{
+			using var img = Image.Load(_stream);
+			var height = img.Height;
+			var width = img.Width;
+			if (SkipWatermarkForSmallImages && (height < Math.Sqrt(SmallImagePixelsThreshold) || width < Math.Sqrt(SmallImagePixelsThreshold)))
+			{
+				return _stream as PooledMemoryStream ?? _stream.SaveAsMemoryStream();
+			}
+
+			var watermark = Image.Load(watermarkImage);
+			watermark.Mutate(c => c.Resize(new ResizeOptions()
+			{
+				Size = new Size
+				{
+					Width = width / 10,
+					Height = height / 10,
+				},
+				Mode = ResizeMode.Pad,
+				Sampler = new BicubicResampler()
+			}));
+			int x, y;
+			padding += (width - 1000) / 100;
+			switch (watermarkPosition)
+			{
+				case WatermarkPosition.TopRight:
+					x = width - watermark.Width - padding;
+					y = padding;
+					break;
+
+				case WatermarkPosition.BottomLeft:
+					x = padding;
+					y = height - watermark.Height - padding;
+					break;
+
+				case WatermarkPosition.BottomRight:
+					x = width - watermark.Width - padding;
+					y = height - watermark.Height - padding;
+					break;
+
+				case WatermarkPosition.Center:
+					x = (img.Width - watermark.Width) / 2;
+					y = (img.Height - watermark.Height) / 2;
+					break;
+
+				default:
+					x = padding;
+					y = padding;
+					break;
+			}
+
+			img.Mutate(c =>
+			{
+				c.DrawImage(watermark, new Point(x, y), opacity);
+				watermark.Dispose();
+			});
+			var ms = new PooledMemoryStream();
+			img.SaveAsWebp(ms);
+			ms.Position = 0;
+			return ms;
+		}
+	}
 }

+ 109 - 108
Masuit.Tools.Abstractions/Strings/ValidateCode.cs

@@ -5,6 +5,7 @@ using System.Text;
 using Masuit.Tools.Systems;
 using SixLabors.Fonts;
 using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.Drawing;
 using SixLabors.ImageSharp.PixelFormats;
 using SixLabors.ImageSharp.Processing;
 using SixLabors.ImageSharp.Drawing.Processing;
@@ -12,126 +13,126 @@ using SixLabors.ImageSharp.Formats.Webp;
 
 namespace Masuit.Tools.Strings
 {
-    /// <summary>
-    /// 画验证码
-    /// </summary>
-    public static class ValidateCode
-    {
-        /// <summary>
-        /// 生成验证码
-        /// </summary>
-        /// <param name="length">指定验证码的长度</param>
-        /// <returns>验证码字符串</returns>
-        public static string CreateValidateCode(int length)
-        {
-            string ch = "abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ1234567890@#$%&?";
-            byte[] b = new byte[4];
-            using var cpt = RandomNumberGenerator.Create();
-            cpt.GetBytes(b);
-            var r = new Random(BitConverter.ToInt32(b, 0));
-            var sb = new StringBuilder();
-            for (int i = 0; i < length; i++)
-            {
-                sb.Append(ch[r.StrictNext(ch.Length)]);
-            }
+	/// <summary>
+	/// 画验证码
+	/// </summary>
+	public static class ValidateCode
+	{
+		/// <summary>
+		/// 生成验证码
+		/// </summary>
+		/// <param name="length">指定验证码的长度</param>
+		/// <returns>验证码字符串</returns>
+		public static string CreateValidateCode(int length)
+		{
+			string ch = "abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ1234567890@#$%&?";
+			byte[] b = new byte[4];
+			using var cpt = RandomNumberGenerator.Create();
+			cpt.GetBytes(b);
+			var r = new Random(BitConverter.ToInt32(b, 0));
+			var sb = new StringBuilder();
+			for (int i = 0; i < length; i++)
+			{
+				sb.Append(ch[r.StrictNext(ch.Length)]);
+			}
 
-            return sb.ToString();
-        }
+			return sb.ToString();
+		}
 
-        /// <summary>
-        /// 创建验证码的图片
-        /// </summary>
-        /// <param name="validateCode">验证码序列</param>
-        /// <param name="fontSize">字体大小,默认值28px</param>
-        /// <exception cref="Exception">The operation failed.</exception>
-        public static PooledMemoryStream CreateValidateGraphic(this string validateCode, int fontSize = 28)
-        {
-            var fontFamily = SystemFonts.Families.Where(f => new[] { "Consolas", "DejaVu Sans", "KaiTi", "NSimSun", "SimSun", "SimHei", "Microsoft YaHei UI", "Arial" }.Contains(f.Name)).OrderByRandom().FirstOrDefault();
-            if (fontFamily == default)
-            {
-                fontFamily = SystemFonts.Families.OrderByRandom().FirstOrDefault();
-            }
+		/// <summary>
+		/// 创建验证码的图片
+		/// </summary>
+		/// <param name="validateCode">验证码序列</param>
+		/// <param name="fontSize">字体大小,默认值28px</param>
+		/// <exception cref="Exception">The operation failed.</exception>
+		public static PooledMemoryStream CreateValidateGraphic(this string validateCode, int fontSize = 28)
+		{
+			var fontFamily = SystemFonts.Families.Where(f => new[] { "Consolas", "DejaVu Sans", "KaiTi", "NSimSun", "SimSun", "SimHei", "Microsoft YaHei UI", "Arial" }.Contains(f.Name)).OrderByRandom().FirstOrDefault();
+			if (fontFamily == default)
+			{
+				fontFamily = SystemFonts.Families.OrderByRandom().FirstOrDefault();
+			}
 
-            var font = fontFamily.CreateFont(fontSize);
-            var measure = TextMeasurer.Measure(validateCode, new TextOptions(font));
-            var width = (int)Math.Ceiling(measure.Width * 1.5);
-            var height = (int)Math.Ceiling(measure.Height + 5);
-            using var image = new Image<Rgba32>(width, height);
+			var font = fontFamily.CreateFont(fontSize);
+			var measure = TextMeasurer.MeasureSize(validateCode, new TextOptions(font));
+			var width = (int)Math.Ceiling(measure.Width + 15);
+			var height = (int)Math.Ceiling(measure.Height + 15);
+			using var image = new Image<Rgba32>(width, height);
 
-            //生成随机生成器
-            Random random = new Random();
+			//生成随机生成器
+			Random random = new Random();
 
-            //清空图片背景色
-            image.Mutate(g =>
-            {
-                g.BackgroundColor(Color.White);
+			//清空图片背景色
+			image.Mutate(g =>
+			{
+				g.BackgroundColor(Color.White);
 
-                //画图片的干扰线
-                for (int i = 0; i < 75; i++)
-                {
-                    int x1 = random.StrictNext(width);
-                    int x2 = random.StrictNext(width);
-                    int y1 = random.StrictNext(height);
-                    int y2 = random.StrictNext(height);
-                    g.DrawLines(new Pen(new Color(new Rgba32((byte)random.StrictNext(255), (byte)random.StrictNext(255), (byte)random.StrictNext(255))), 1), new PointF(x1, y1), new PointF(x2, y2));
-                }
+				//画图片的干扰线
+				for (int i = 0; i < 65; i++)
+				{
+					int x1 = random.StrictNext(width);
+					int x2 = random.StrictNext(width);
+					int y1 = random.StrictNext(height);
+					int y2 = random.StrictNext(height);
+					g.Draw(new SolidPen(new Color(new Rgba32((byte)random.StrictNext(255), (byte)random.StrictNext(255), (byte)random.StrictNext(255))), 1), new Path(new[] { new PointF(x1, y1), new PointF(x2, y2) }));
+				}
 
-                //渐变.
-                var brush = new LinearGradientBrush(new PointF(0, 0), new PointF(width, height), GradientRepetitionMode.Repeat, new ColorStop(0.5f, Color.Blue), new ColorStop(0.5f, Color.DarkRed));
-                g.DrawText(validateCode, font, brush, new PointF(3, 2));
+				//渐变.
+				var brush = new LinearGradientBrush(new PointF(0, 0), new PointF(width, height), GradientRepetitionMode.Repeat, new ColorStop(0.5f, Color.Blue), new ColorStop(0.5f, Color.DarkRed));
+				g.DrawText(validateCode, font, brush, new PointF(3, 2));
 
-                //画图片的边框线
-                g.DrawLines(new Pen(Color.Silver, 1), new PointF(0, 0), new PointF(width - 1, height - 1));
-            });
+				//画图片的边框线
+				g.Draw(Color.Silver, 1, new RectangleF(0, 0, width - 1, height - 1));
+			});
 
-            //画图片的前景干扰点
-            for (int i = 0; i < 350; i++)
-            {
-                int x = random.StrictNext(image.Width);
-                int y = random.StrictNext(image.Height);
-                image[x, y] = new Rgba32(random.StrictNext(255), random.StrictNext(255), random.StrictNext(255));
-            }
+			//画图片的前景干扰点
+			for (int i = 0; i < 350; i++)
+			{
+				int x = random.StrictNext(image.Width);
+				int y = random.StrictNext(image.Height);
+				image[x, y] = new Rgba32(random.StrictNext(255), random.StrictNext(255), random.StrictNext(255));
+			}
 
-            //保存图片数据
-            var stream = new PooledMemoryStream();
-            image.Save(stream, WebpFormat.Instance);
-            stream.Position = 0;
-            return stream;
-        }
+			//保存图片数据
+			var stream = new PooledMemoryStream();
+			image.Save(stream, WebpFormat.Instance);
+			stream.Position = 0;
+			return stream;
+		}
 
-        /// <summary>
-        /// 字符串宽度
-        /// </summary>
-        /// <param name="s"></param>
-        /// <param name="fontSize"></param>
-        /// <returns></returns>
-        public static float StringWidth(this string s, int fontSize = 1)
-        {
-            var fontFamily = SystemFonts.Families.FirstOrDefault(f => f.Name == "Microsoft YaHei UI");
-            if (fontFamily == default)
-            {
-                fontFamily = SystemFonts.Families.FirstOrDefault();
-            }
+		/// <summary>
+		/// 字符串宽度
+		/// </summary>
+		/// <param name="s"></param>
+		/// <param name="fontSize"></param>
+		/// <returns></returns>
+		public static float StringWidth(this string s, int fontSize = 1)
+		{
+			var fontFamily = SystemFonts.Families.FirstOrDefault(f => f.Name == "Microsoft YaHei UI");
+			if (fontFamily == default)
+			{
+				fontFamily = SystemFonts.Families.FirstOrDefault();
+			}
 
-            return TextMeasurer.Measure(s, new TextOptions(fontFamily.CreateFont(fontSize))).Width;
-        }
+			return TextMeasurer.MeasureSize(s, new TextOptions(fontFamily.CreateFont(fontSize))).Width;
+		}
 
-        /// <summary>
-        /// 字符串宽度
-        /// </summary>
-        /// <param name="s"></param>
-        /// <param name="fontName">字体名字,如:Microsoft YaHei UI</param>
-        /// <param name="fontSize"></param>
-        /// <returns></returns>
-        public static float StringWidth(this string s, string fontName, int fontSize = 1)
-        {
-            var fontFamily = SystemFonts.Families.FirstOrDefault(f => f.Name == fontName);
-            if (fontFamily == default)
-            {
-                throw new ArgumentException($"字体 {fontName} 不存在,请尝试其它字体!");
-            }
+		/// <summary>
+		/// 字符串宽度
+		/// </summary>
+		/// <param name="s"></param>
+		/// <param name="fontName">字体名字,如:Microsoft YaHei UI</param>
+		/// <param name="fontSize"></param>
+		/// <returns></returns>
+		public static float StringWidth(this string s, string fontName, int fontSize = 1)
+		{
+			var fontFamily = SystemFonts.Families.FirstOrDefault(f => f.Name == fontName);
+			if (fontFamily == default)
+			{
+				throw new ArgumentException($"字体 {fontName} 不存在,请尝试其它字体!");
+			}
 
-            return TextMeasurer.Measure(s, new TextOptions(fontFamily.CreateFont(fontSize))).Width;
-        }
-    }
+			return TextMeasurer.MeasureSize(s, new TextOptions(fontFamily.CreateFont(fontSize))).Width;
+		}
+	}
 }

+ 27 - 0
Masuit.Tools.Abstractions/Win32/AntiVirus/AmsiResult.cs

@@ -0,0 +1,27 @@
+namespace Masuit.Tools.Win32.AntiVirus;
+
+public enum AmsiResult
+{
+    /// <summary>
+    /// 正常文件
+    /// </summary>
+    AmsiResultClean = 0,
+
+    /// <summary>
+    /// 正常文件
+    /// </summary>
+    AmsiResultNotDetected = 1,
+
+    AmsiResultBlockedByAdminStart = 16384,
+    AmsiResultBlockedByAdminEnd = 20479,
+
+    /// <summary>
+    /// 非正常文件
+    /// </summary>
+    AmsiResultDetected = 32768,
+
+    /// <summary>
+    /// 扫描失败
+    /// </summary>
+    ScanException = -1
+}

+ 185 - 0
Masuit.Tools.Abstractions/Win32/AntiVirus/AmsiScanService.cs

@@ -0,0 +1,185 @@
+using System;
+using System.Buffers;
+using System.IO;
+using static Masuit.Tools.Win32.AntiVirus.AmsiWrapper;
+
+namespace Masuit.Tools.Win32.AntiVirus;
+
+public class AmsiScanService
+{
+    private readonly string AppName = "AMSI";
+
+    public AmsiScanService()
+    {
+        if (!File.Exists(SystemParameter.AmsiDllPath))
+        {
+            throw new PlatformNotSupportedException("amsi.dll not found");
+        }
+    }
+
+    /// <summary>
+    /// 扫描文件
+    /// </summary>
+    /// <param name="filePath"></param>
+    /// <returns></returns>
+    public ScanResult Scan(string filePath)
+    {
+        if (!File.Exists(filePath))
+        {
+            throw new FileNotFoundException();
+        }
+
+        try
+        {
+            AmsiInitialize(AppName, out var amsiContext);
+            AmsiOpenSession(amsiContext, out var amsiSession);
+
+            //读取文件(如文件有可疑,会引发Exception)
+            var fileData = File.ReadAllBytes(filePath);
+            return ScanBuffer(amsiContext, amsiSession, fileData, Path.GetFileName(filePath));
+        }
+        catch (Exception ex)
+        {
+            if (ex.Message.Contains("Operation did not complete successfully because the file contains a virus or potentially unwanted software"))
+            {
+                return new ScanResult
+                {
+                    Result = ResultCode.Detected
+                };
+            }
+
+            return new ScanResult
+            {
+                Result = ResultCode.Exception,
+                Msg = ex.Message
+            };
+        }
+    }
+
+    /// <summary>
+    /// 扫描文件流
+    /// </summary>
+    /// <param name="fileStream"></param>
+    /// <returns></returns>
+    public ScanResult Scan(Stream fileStream)
+    {
+        if (fileStream == null)
+        {
+            throw new ArgumentNullException(nameof(fileStream));
+        }
+
+        var fileData = ArrayPool<byte>.Shared.Rent((int)fileStream.Length);
+        try
+        {
+            AmsiInitialize(AppName, out var amsiContext);
+            AmsiOpenSession(amsiContext, out var amsiSession);
+            _ = fileStream.Read(fileData, 0, (int)fileStream.Length);
+            return ScanBuffer(amsiContext, amsiSession, fileData, "");
+        }
+        catch (Exception ex)
+        {
+            if (ex.Message.Contains("Operation did not complete successfully because the file contains a virus or potentially unwanted software"))
+            {
+                return new ScanResult
+                {
+                    Result = ResultCode.Detected
+                };
+            }
+
+            return new ScanResult
+            {
+                Result = ResultCode.Exception,
+                Msg = ex.Message
+            };
+        }
+        finally
+        {
+            ArrayPool<byte>.Shared.Return(fileData);
+        }
+    }
+
+    /// <summary>
+    /// 扫描文件
+    /// </summary>
+    /// <param name="fileData"></param>
+    /// <returns></returns>
+    public ScanResult Scan(byte[] fileData)
+    {
+        if (fileData == null)
+        {
+            throw new ArgumentNullException(nameof(fileData));
+        }
+
+        try
+        {
+            AmsiInitialize(AppName, out nint amsiContext);
+            AmsiOpenSession(amsiContext, out nint amsiSession);
+            return ScanBuffer(amsiContext, amsiSession, fileData, "");
+        }
+        catch (Exception ex)
+        {
+            if (ex.Message.Contains("Operation did not complete successfully because the file contains a virus or potentially unwanted software"))
+            {
+                return new ScanResult
+                {
+                    Result = ResultCode.Detected
+                };
+            }
+
+            return new ScanResult
+            {
+                Result = ResultCode.Exception,
+                Msg = ex.Message
+            };
+        }
+    }
+
+    /// <summary>
+    /// 扫描文件Buffer
+    /// </summary>
+    /// <param name="amsiContext"></param>
+    /// <param name="amsiSession"></param>
+    /// <param name="bufferData"></param>
+    /// <param name="fileName"></param>
+    /// <returns></returns>
+    private ScanResult ScanBuffer(nint amsiContext, nint amsiSession, byte[] bufferData, string fileName)
+    {
+        AmsiScanBuffer(amsiContext, bufferData, (uint)bufferData.Length, fileName, amsiSession, out AmsiResult amsiResult);
+        var result = ConvertToScanResult(amsiResult);
+
+        //if (result.Result == ResultCode.NotDetected)
+        //{
+        //    var content = Encoding.ASCII.GetString(bufferData);
+        //    AmsiScanString(amsiContext, content, fileName, amsiSession, out amsiResult);
+        //    return ConvertToScanResult(amsiResult);
+        //}
+
+        return result;
+    }
+
+    private ScanResult ConvertToScanResult(AmsiResult amsiResult)
+    {
+        switch (amsiResult)
+        {
+            case AmsiResult.AmsiResultClean:
+            case AmsiResult.AmsiResultNotDetected:
+                return new ScanResult
+                {
+                    Result = ResultCode.NotDetected
+                };
+
+            case AmsiResult.AmsiResultDetected:
+                return new ScanResult
+                {
+                    Result = ResultCode.Detected
+                };
+
+            default:
+                return new ScanResult
+                {
+                    Result = ResultCode.Exception,
+                    Msg = "Blocked behavior,scan not allowed!"
+                };
+        }
+    }
+}

+ 25 - 0
Masuit.Tools.Abstractions/Win32/AntiVirus/AmsiWrapper.cs

@@ -0,0 +1,25 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Masuit.Tools.Win32.AntiVirus;
+
+internal static class AmsiWrapper
+{
+    [DllImport("Amsi.dll", EntryPoint = "AmsiInitialize", CallingConvention = CallingConvention.StdCall)]
+    public static extern int AmsiInitialize([MarshalAs(UnmanagedType.LPWStr)] string appName, out nint amsiContext);
+
+    [DllImport("Amsi.dll", EntryPoint = "AmsiUninitialize", CallingConvention = CallingConvention.StdCall)]
+    public static extern void AmsiUninitialize(nint amsiContext);
+
+    [DllImport("Amsi.dll", EntryPoint = "AmsiOpenSession", CallingConvention = CallingConvention.StdCall)]
+    public static extern int AmsiOpenSession(nint amsiContext, out nint session);
+
+    [DllImport("Amsi.dll", EntryPoint = "AmsiCloseSession", CallingConvention = CallingConvention.StdCall)]
+    public static extern void AmsiCloseSession(nint amsiContext, nint session);
+
+    [DllImport("Amsi.dll", EntryPoint = "AmsiScanBuffer", CallingConvention = CallingConvention.StdCall)]
+    public static extern int AmsiScanBuffer(nint amsiContext, byte[] buffer, uint length, string contentName, nint session, out AmsiResult result);
+
+    [DllImport("Amsi.dll", EntryPoint = "AmsiScanString", CallingConvention = CallingConvention.StdCall)]
+    public static extern int AmsiScanString(nint amsiContext, [In()][MarshalAs(UnmanagedType.LPWStr)] string @string, [In()][MarshalAs(UnmanagedType.LPWStr)] string contentName, nint session, out AmsiResult result);
+}

+ 35 - 0
Masuit.Tools.Abstractions/Win32/AntiVirus/ScanResult.cs

@@ -0,0 +1,35 @@
+using System;
+
+namespace Masuit.Tools.Win32.AntiVirus;
+
+public class ScanResult
+{
+    /// <summary>
+    /// 扫描结果
+    /// </summary>
+    public ResultCode Result { get; set; }
+
+    /// <summary>
+    ///
+    /// </summary>
+    public string Msg { get; set; }
+}
+
+[Flags]
+public enum ResultCode
+{
+    /// <summary>
+    /// 未检测到
+    /// </summary>
+    NotDetected,
+
+    /// <summary>
+    /// 检测到
+    /// </summary>
+    Detected,
+
+    /// <summary>
+    /// 异常
+    /// </summary>
+    Exception
+}

+ 8 - 0
Masuit.Tools.Abstractions/Win32/AntiVirus/SystemParameter.cs

@@ -0,0 +1,8 @@
+namespace Masuit.Tools.Win32.AntiVirus;
+
+public class SystemParameter
+{
+    public const string WindowsDefenderPath = @"C:\Program Files\Windows Defender";
+    public const string WindowsDefenderExeName = "MpCmdRun.exe";
+    public const string AmsiDllPath = @"C:\Windows\System32\amsi.dll";
+}

+ 10 - 0
Masuit.Tools.Abstractions/Win32/AntiVirus/WindowsDefenderScanException.cs

@@ -0,0 +1,10 @@
+using System;
+
+namespace Masuit.Tools.Win32.AntiVirus;
+
+public class WindowsDefenderScanException : Exception
+{
+    public WindowsDefenderScanException() : base("Execute scan command exception")
+    {
+    }
+}

+ 171 - 0
Masuit.Tools.Abstractions/Win32/AntiVirus/WindowsDefenderScanService.cs

@@ -0,0 +1,171 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using Masuit.Tools.Files;
+using Masuit.Tools.Systems;
+
+namespace Masuit.Tools.Win32.AntiVirus;
+
+public class WindowsDefenderScanService
+{
+	public WindowsDefenderScanService()
+	{
+		if (!Directory.Exists(SystemParameter.WindowsDefenderPath))
+		{
+			throw new PlatformNotSupportedException("Windows Defender not found");
+		}
+
+		if (!File.Exists($"{SystemParameter.WindowsDefenderPath}\\{SystemParameter.WindowsDefenderExeName}"))
+		{
+			throw new PlatformNotSupportedException("Windows Defender not found");
+		}
+	}
+
+	/// <summary>
+	/// 扫描文件流
+	/// </summary>
+	/// <param name="filePath"></param>
+	public ScanResult ScanStream(Stream stream)
+	{
+		var temp = Path.Combine(Environment.GetEnvironmentVariable("temp"), SnowFlake.NewId);
+		stream.SaveFile(temp);
+		if (stream.CanSeek)
+		{
+			stream.Position = 0;
+		}
+		return ScanFile(temp);
+	}
+
+	/// <summary>
+	/// 扫描文件
+	/// </summary>
+	/// <param name="filePath"></param>
+	public ScanResult ScanFile(string filePath)
+	{
+		if (!File.Exists(filePath))
+		{
+			throw new FileNotFoundException();
+		}
+		try
+		{
+			//Scanning xxxxx  found 1 threats.
+			//Scanning xxxxx  found no threats.
+			var result = RunScanCommand(filePath);
+			if (result.Contains("found no threats"))
+			{
+				return new ScanResult
+				{
+					Result = ResultCode.NotDetected,
+				};
+			}
+
+			return new ScanResult
+			{
+				Result = ResultCode.Detected,
+			};
+		}
+		catch (Exception ex)
+		{
+			return new ScanResult
+			{
+				Result = ResultCode.Exception,
+				Msg = ex.Message
+			};
+		}
+	}
+
+	/// <summary>
+	/// 扫描文件夹(不支持递文件夹嵌套文件夹的扫描)
+	/// </summary>
+	/// <param name="directoryPath"></param>
+	/// <returns>如有威胁文件,只返回文件夹中有威胁的文件</returns>
+	public ScanResult ScanDirectory(string directoryPath)
+	{
+		if (!Directory.Exists(directoryPath))
+		{
+			throw new DirectoryNotFoundException();
+		}
+		try
+		{
+			var files = Directory.GetFiles(directoryPath);
+
+			//文件夹扫描
+			var result = RunScanCommand(directoryPath);
+
+			if (result.Contains("found no threats"))
+			{
+				return new ScanResult
+				{
+					Result = ResultCode.NotDetected,
+				};
+			}
+
+			result = result.ToLower();
+			var detectedFile = (from file in files
+								let filePath = $"{file}\r\n"
+								where result.Contains(filePath.ToLower())
+								select file.Replace(directoryPath, "").Replace("\\", "")).ToList();
+
+			//解析文件,找到有威胁的文件
+			return new ScanResult
+			{
+				Result = ResultCode.Detected,
+				Msg = string.Join(";", detectedFile)
+			};
+		}
+		catch (Exception ex)
+		{
+			return new ScanResult
+			{
+				Result = ResultCode.Exception,
+				Msg = ex.Message
+			};
+		}
+	}
+
+	/// <summary>
+	/// 运行命令
+	/// </summary>
+	/// <param name="path"></param>
+	/// <returns></returns>
+	private string RunScanCommand(string path)
+	{
+		if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+		{
+			throw new PlatformNotSupportedException();
+		}
+
+		var proc = new Process();
+		try
+		{
+			proc.StartInfo.FileName = "cmd.exe";
+			proc.StartInfo.UseShellExecute = false;
+			proc.StartInfo.RedirectStandardInput = true;
+			proc.StartInfo.RedirectStandardOutput = true;
+			proc.StartInfo.RedirectStandardError = true;
+			proc.StartInfo.CreateNoWindow = true;
+			proc.Start();
+
+			var command = $"\"{SystemParameter.WindowsDefenderPath}\\{SystemParameter.WindowsDefenderExeName}\" -Scan -ScanType 3 -File \"{path}\" -DisableRemediation";
+			proc.StandardInput.WriteLine(command);
+			proc.StandardInput.WriteLine("exit");
+			while (!proc.HasExited)
+			{
+				proc.WaitForExit(1000);
+			}
+
+			return proc.StandardOutput.ReadToEnd();
+		}
+		catch (Exception ex)
+		{
+			throw new WindowsDefenderScanException();
+		}
+		finally
+		{
+			proc.Close();
+			proc.Dispose();
+		}
+	}
+}

+ 1 - 1
Masuit.Tools.AspNetCore/Masuit.Tools.AspNetCore.csproj

@@ -18,7 +18,7 @@
         <Product>Masuit.Tools.AspNetCore</Product>
         <PackageId>Masuit.Tools.AspNetCore</PackageId>
         <LangVersion>latest</LangVersion>
-        <Version>1.2.4.1</Version>
+        <Version>1.2.5</Version>
         <RepositoryType></RepositoryType>
         <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
         <FileVersion>1.1.9</FileVersion>

+ 1 - 1
Masuit.Tools.Core/Masuit.Tools.Core.csproj

@@ -6,7 +6,7 @@
 官网教程:https://tools.masuit.org
 github:https://github.com/ldqk/Masuit.Tools
         </Description>
-        <Version>2.6.4.1</Version>
+        <Version>2.6.5</Version>
         <Copyright>Copyright © 懒得勤快</Copyright>
         <PackageProjectUrl>https://github.com/ldqk/Masuit.Tools</PackageProjectUrl>
         <PackageTags>Masuit.Tools,工具库,Utility,Crypt,Extensions</PackageTags>

+ 40 - 0
Masuit.Tools.Core/Windows/RegisterFileSecurityScanSersvice.cs

@@ -0,0 +1,40 @@
+using Masuit.Tools.Win32.AntiVirus;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Masuit.Tools.Windows.AntiVirus;
+
+public static class RegisterFileSecurityScanSersvice
+{
+	/// <summary>
+	///  注入Windows Defender服务
+	/// </summary>
+	/// <param name="services"></param>
+	public static void AddWindowsDefender(this IServiceCollection services)
+	{
+		if (!Directory.Exists(SystemParameter.WindowsDefenderPath))
+		{
+			throw new PlatformNotSupportedException("Windows Defender not found");
+		}
+
+		if (!File.Exists($"{SystemParameter.WindowsDefenderPath}\\{SystemParameter.WindowsDefenderExeName}"))
+		{
+			throw new PlatformNotSupportedException("Windows Defender not found");
+		}
+
+		services.AddSingleton<WindowsDefenderScanService>();
+	}
+
+	/// <summary>
+	/// 注入AMSI服务
+	/// </summary>
+	/// <param name="services"></param>
+	public static void AddAMSI(this IServiceCollection services)
+	{
+		if (!File.Exists(SystemParameter.AmsiDllPath))
+		{
+			throw new PlatformNotSupportedException("amsi.dll not found");
+		}
+
+		services.AddSingleton<AmsiScanService>();
+	}
+}

+ 609 - 609
Masuit.Tools.Excel/ExcelExtension.cs

@@ -15,617 +15,617 @@ namespace Masuit.Tools.Excel;
 
 public static class ExcelExtension
 {
-    static ExcelExtension()
-    {
-        ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
-    }
+	static ExcelExtension()
+	{
+		ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
+	}
 
 #if NET5_0_OR_GREATER
-    [System.Runtime.CompilerServices.ModuleInitializer]
-    internal static void Init()
-    {
-        ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
-    }
+	[System.Runtime.CompilerServices.ModuleInitializer]
+	internal static void Init()
+	{
+		ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
+	}
 #endif
 
-    /// <summary>
-    /// 将内存表自动填充到Excel
-    /// </summary>
-    /// <param name="sheetTables">sheet名和内存表的映射</param>
-    /// <param name="password">密码</param>
-    /// <param name="settings">列设置</param>
-    /// <returns>内存流</returns>
-    public static PooledMemoryStream ToExcel(this Dictionary<string, DataTable> sheetTables, string password = null, ColumnSettings settings = null)
-    {
-        using var pkg = new ExcelPackage();
-        foreach (var pair in sheetTables)
-        {
-            pair.Value.TableName = pair.Key;
-            CreateWorksheet(pkg, pair.Value, settings);
-        }
-
-        return SaveAsStream(pkg, password);
-    }
-
-    /// <summary>
-    /// 将内存表自动填充到Excel
-    /// </summary>
-    /// <param name="sheetTables">sheet名和内存表的映射</param>
-    /// <param name="password">密码</param>
-    /// <param name="settings">列设置</param>
-    /// <returns>内存流</returns>
-    public static PooledMemoryStream ToExcel<T>(this Dictionary<string, IEnumerable<T>> sheetTables, string password = null, ColumnSettings settings = null)
-    {
-        using var pkg = new ExcelPackage();
-        foreach (var pair in sheetTables)
-        {
-            CreateWorksheet(pkg, pair, settings);
-        }
-
-        return SaveAsStream(pkg, password);
-    }
-
-    /// <summary>
-    /// 将内存表自动填充到Excel
-    /// </summary>
-    /// <param name="tables">内存表</param>
-    /// <param name="password">密码</param>
-    /// <returns>内存流</returns>
-    public static PooledMemoryStream ToExcel(this List<DataTable> tables, string password = null, ColumnSettings settings = null)
-    {
-        using var pkg = new ExcelPackage();
-        foreach (var table in tables)
-        {
-            CreateWorksheet(pkg, table, settings);
-        }
-
-        return SaveAsStream(pkg, password);
-    }
-
-    /// <summary>
-    /// 将内存表自动填充到Excel
-    /// </summary>
-    /// <param name="table">内存表</param>
-    /// <param name="password">密码</param>
-    /// <returns>内存流</returns>
-    public static PooledMemoryStream ToExcel(this DataTable table, string password = null, ColumnSettings settings = null)
-    {
-        using var pkg = new ExcelPackage();
-        CreateWorksheet(pkg, table, settings);
-        return SaveAsStream(pkg, password);
-    }
-
-    /// <summary>
-    /// 将内存表自动填充到Excel
-    /// </summary>
-    /// <param name="table">内存表</param>
-    /// <param name="password">密码</param>
-    /// <returns>内存流</returns>
-    public static PooledMemoryStream ToExcel<T>(this IEnumerable<T> table, string password = null, ColumnSettings settings = null)
-    {
-        using var pkg = new ExcelPackage();
-        CreateWorksheet(pkg, new KeyValuePair<string, IEnumerable<T>>("Sheet1", table), settings);
-        return SaveAsStream(pkg, password);
-    }
-
-    private static PooledMemoryStream SaveAsStream(ExcelPackage pkg, string password)
-    {
-        var ms = new PooledMemoryStream();
-        if (!string.IsNullOrEmpty(password))
-        {
-            pkg.SaveAs(ms, password);
-        }
-        else
-        {
-            pkg.SaveAs(ms);
-        }
-
-        ms.Position = 0;
-        return ms;
-    }
-
-    public static void CreateWorksheet(this ExcelPackage pkg, DataTable table, ColumnSettings settings = null)
-    {
-        if (string.IsNullOrEmpty(table.TableName))
-        {
-            table.TableName = "Sheet1";
-        }
-
-        var sheet = pkg.Workbook.Worksheets.Add(table.TableName);
-        sheet.Cells.Style.Font.Name = "微软雅黑";
-
-        FillWorksheet(sheet, table, settings);
-
-        sheet.Cells.Style.VerticalAlignment = ExcelVerticalAlignment.Center;
-
-        //打印方向:纵向
-        sheet.PrinterSettings.Orientation = eOrientation.Landscape;
-
-        //集中在一页里打印
-        sheet.PrinterSettings.FitToPage = true;
-
-        //使用A4纸
-        sheet.PrinterSettings.PaperSize = ePaperSize.A4;
-    }
-
-    public static void CreateWorksheet<T>(this ExcelPackage pkg, KeyValuePair<string, IEnumerable<T>> table, ColumnSettings settings = null)
-    {
-        var sheet = pkg.Workbook.Worksheets.Add(table.Key);
-        sheet.Cells.Style.Font.Name = "微软雅黑";
-
-        FillWorksheet(sheet, table.Value, settings);
-
-        sheet.Cells.Style.VerticalAlignment = ExcelVerticalAlignment.Center;
-
-        //打印方向:纵向
-        sheet.PrinterSettings.Orientation = eOrientation.Landscape;
-
-        //集中在一页里打印
-        sheet.PrinterSettings.FitToPage = true;
-
-        //使用A4纸
-        sheet.PrinterSettings.PaperSize = ePaperSize.A4;
-    }
-
-    /// <summary>
-    /// 从datatable填充工作簿
-    /// </summary>
-    /// <param name="sheet">工作簿</param>
-    /// <param name="table">数据</param>
-    /// <param name="settings">列设置</param>
-    /// <param name="startRow">起始行,默认第一行</param>
-    /// <param name="startColumn">起始列,默认第一列A列</param>
-    public static void FillWorksheet(this ExcelWorksheet sheet, DataTable table, ColumnSettings settings = null, int startRow = 1, int startColumn = 1)
-    {
-        var hasPicColumn = false;
-        if (table.Rows.Count > 0)
-        {
-            for (int i = 0; i < table.Columns.Count; i++)
-            {
-                switch (table.Rows[0][i])
-                {
-                    case Stream:
-                    case IEnumerable<Stream>:
-                    case IDictionary<string, Stream>:
-                    case IDictionary<string, MemoryStream>:
-                        hasPicColumn = true;
-                        break;
-                }
-            }
-
-            if (hasPicColumn)
-            {
-                // 填充表头
-                var maxWidth = new int[table.Columns.Count];
-                for (var j = 0; j < table.Columns.Count; j++)
-                {
-                    sheet.SetValue(startRow, j + startColumn, table.Columns[j].ColumnName);
-                    maxWidth[j] = Encoding.UTF8.GetBytes(table.Columns[j].ColumnName).Length;
-                }
-
-                sheet.Row(startRow).Style.Font.Bold = true; // 表头设置为粗体
-                sheet.Row(startRow).Style.Font.Size = sheet.Row(startRow).Style.Font.Size * 1.11f; // 表头字号放大1.11倍
-                sheet.Row(startRow).Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
-                sheet.Row(startRow).CustomHeight = true; // 自动调整行高
-                if (settings != null)
-                {
-                    foreach (var x in settings.ColumnTypes)
-                    {
-                        sheet.Column(x.Key).Style.Numberformat.Format = x.Value;
-                    }
-                }
-                sheet.Cells.AutoFitColumns(); // 表头自适应列宽
-
-                // 填充内容
-                for (int i = 0; i < table.Rows.Count; i++)
-                {
-                    sheet.Row(i + startRow + 1).CustomHeight = true; // 自动调整行高
-                    for (int j = 0; j < table.Columns.Count; j++)
-                    {
-                        switch (table.Rows[i][j])
-                        {
-                            case Stream s:
-                                {
-                                    if (s.Length > 2)
-                                    {
-                                        try
-                                        {
-                                            var picture = sheet.Drawings.AddPicture(Guid.NewGuid().ToString(), s);
-                                            picture.SetPosition(i + startRow, 3, j + startColumn - 1, 5); //设置图片显示位置
-                                            var percent = Math.Round(Math.Min(120f / picture.Image.Bounds.Height * picture.Image.Bounds.VerticalResolution, 100));
-                                            sheet.Row(i + startRow + 1).Height = 90;
-                                            sheet.Column(j + startColumn).Width = Math.Max(sheet.Column(j + startColumn).Width, picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent / 6.4);
-                                            picture.SetSize((int)percent);
-                                        }
-                                        catch
-                                        {
-                                            throw new ArgumentException($"{i + startRow}行{j}列图像格式不受支持");
-                                        }
-                                    }
-
-                                    sheet.SetValue(i + startRow + 1, j + startColumn, "");
-
-                                    break;
-                                }
-
-                            case IEnumerable<Stream> streams:
-                                {
-                                    double sumWidth = 0;
-                                    int index = 0;
-                                    foreach (var stream in streams.Where(stream => stream.Length > 2))
-                                    {
-                                        try
-                                        {
-                                            var picture = sheet.Drawings.AddPicture(Guid.NewGuid().ToString(), stream);
-                                            var percent = Math.Round(Math.Min(120f / picture.Image.Bounds.Height * picture.Image.Bounds.VerticalResolution, 100));
-                                            picture.SetPosition(i + startRow, 3, j + startColumn - 1, (int)Math.Ceiling(picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent * index++)); //设置图片显示位置
-                                            sheet.Row(i + startRow + 1).Height = 90;
-                                            sumWidth += picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent / 6.4;
-                                            sheet.Column(j + startColumn).Width = Math.Max(sheet.Column(j + startColumn).Width, sumWidth);
-                                            picture.SetSize((int)percent);
-                                        }
-                                        catch
-                                        {
-                                            throw new ArgumentException($"{i + startRow}行{j}列第{index}张图像格式不受支持");
-                                        }
-                                    }
-
-                                    sheet.SetValue(i + startRow + 1, j + startColumn, "");
-                                    break;
-                                }
-
-                            case IDictionary<string, Stream> dic:
-                                {
-                                    double sumWidth = 0;
-                                    int index = 0;
-                                    foreach (var kv in dic.Where(kv => kv.Value.Length > 2))
-                                    {
-                                        try
-                                        {
-                                            var picture = sheet.Drawings.AddPicture(Guid.NewGuid().ToString(), kv.Value, new Uri(kv.Key));
-                                            var percent = Math.Round(Math.Min(120f / picture.Image.Bounds.Height * picture.Image.Bounds.VerticalResolution, 100));
-                                            picture.SetPosition(i + startRow, 3, j + startColumn - 1, (int)Math.Ceiling(picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent * index++)); //设置图片显示位置
-                                            sheet.Row(i + startRow + 1).Height = 90;
-                                            sumWidth += picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent / 6.4;
-                                            sheet.Column(j + startColumn).Width = Math.Max(sheet.Column(j + startColumn).Width, sumWidth);
-                                            picture.SetSize((int)percent);
-                                        }
-                                        catch
-                                        {
-                                            throw new ArgumentException($"{i + startRow}行{j}列第{index}张图像格式不受支持,图片链接:{kv.Key}");
-                                        }
-                                    }
-
-                                    sheet.SetValue(i + startRow + 1, j + startColumn, "");
-                                    break;
-                                }
-
-                            case IDictionary<string, MemoryStream> dic:
-                                {
-                                    double sumWidth = 0;
-                                    int index = 0;
-                                    foreach (var kv in dic.Where(kv => kv.Value.Length > 2))
-                                    {
-                                        try
-                                        {
-                                            var picture = sheet.Drawings.AddPicture(Guid.NewGuid().ToString(), kv.Value, new Uri(kv.Key));
-                                            var percent = Math.Round(Math.Min(120f / picture.Image.Bounds.Height * picture.Image.Bounds.VerticalResolution, 100));
-                                            picture.SetPosition(i + startRow, 3, j + startColumn - 1, (int)Math.Ceiling(picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent * index++)); //设置图片显示位置
-                                            sheet.Row(i + startRow + 1).Height = 90;
-                                            sumWidth += picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent / 6.4;
-                                            sheet.Column(j + startColumn).Width = Math.Max(sheet.Column(j + startColumn).Width, sumWidth);
-                                            picture.SetSize((int)percent);
-                                        }
-                                        catch
-                                        {
-                                            throw new ArgumentException($"{i + startRow}行{j}列第{index}张图像格式不受支持,图片链接:{kv.Key}");
-                                        }
-                                    }
-
-                                    sheet.SetValue(i + startRow + 1, j + startColumn, "");
-                                    break;
-                                }
-
-                            case IDictionary<string, PooledMemoryStream> dic:
-                                {
-                                    double sumWidth = 0;
-                                    int index = 0;
-                                    foreach (var kv in dic.Where(kv => kv.Value.Length > 2))
-                                    {
-                                        try
-                                        {
-                                            var picture = sheet.Drawings.AddPicture(Guid.NewGuid().ToString(), kv.Value, new Uri(kv.Key));
-                                            var percent = Math.Round(Math.Min(120f / picture.Image.Bounds.Height * picture.Image.Bounds.VerticalResolution, 100));
-                                            picture.SetPosition(i + startRow, 3, j + startColumn - 1, (int)Math.Ceiling(picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent * index++)); //设置图片显示位置
-                                            sheet.Row(i + startRow + 1).Height = 90;
-                                            sumWidth += picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent / 6.4;
-                                            sheet.Column(j + startColumn).Width = Math.Max(sheet.Column(j + startColumn).Width, sumWidth);
-                                            picture.SetSize((int)percent);
-                                        }
-                                        catch
-                                        {
-                                            throw new ArgumentException($"{i + startRow}行{j}列第{index}张图像格式不受支持,图片链接:{kv.Key}");
-                                        }
-                                    }
-
-                                    sheet.SetValue(i + startRow + 1, j + startColumn, "");
-                                    break;
-                                }
-
-                            default:
-                                {
-                                    sheet.SetValue(i + startRow + 1, j + startColumn, table.Rows[i][j] ?? "");
-                                    if (table.Rows[i][j] is ValueType)
-                                    {
-                                        sheet.Column(j + startColumn).AutoFit();
-                                    }
-                                    else
-                                    {
-                                        // 根据单元格内容长度来自适应调整列宽
-                                        var fontFamily = SystemFonts.Families.FirstOrDefault(f => f.Name == sheet.Cells[i + startRow + 1, j + startColumn].Style.Font.Name);
-                                        int measureSize = 1;
-                                        if (fontFamily == default)
-                                        {
-                                            fontFamily = SystemFonts.Families.FirstOrDefault();
-                                            measureSize++;
-                                        }
-
-                                        var width = TextMeasurer.Measure(table.Rows[i][j].ToString(), new TextOptions(fontFamily.CreateFont(measureSize))).Width;
-                                        sheet.Column(j + startColumn).Width = Math.Min(110, Math.Max(width, sheet.Column(j + startColumn).Width));
-                                    }
-
-                                    break;
-                                }
-                        }
-                    }
-                }
-
-                sheet.Cells.Style.WrapText = true;
-            }
-            else
-            {
-                sheet.Cells[startRow, startColumn].LoadFromDataTable(table, true, TableStyles.Light15).AutoFitColumns(12, 90);
-                sheet.Cells.Style.WrapText = true;
-            }
-        }
-    }
-
-    /// <summary>
-    /// 从datatable填充工作簿
-    /// </summary>
-    /// <param name="sheet">工作簿</param>
-    /// <param name="table">数据</param>
-    /// <param name="settings">列设置</param>
-    /// <param name="startRow">起始行,默认第一行</param>
-    /// <param name="startColumn">起始列,默认第一列A列</param>
-    public static void FillWorksheet<T>(this ExcelWorksheet sheet, IEnumerable<T> source, ColumnSettings settings = null, int startRow = 1, int startColumn = 1)
-    {
-        var hasPicColumn = false;
-        var properties = typeof(T).GetProperties();
-        if (source is ICollection<T> table)
-        {
-        }
-        else
-        {
-            table = source.ToList();
-        }
-
-        if (table.Any())
-        {
-            if (properties.Any(t => t.PropertyType.IsSubclassOf(typeof(Stream)) || typeof(IEnumerable<Stream>).IsAssignableFrom(t.PropertyType) || (typeof(IDictionary).IsAssignableFrom(t.PropertyType) && t.PropertyType.GenericTypeArguments[1].IsSubclassOf(typeof(Stream)))))
-            {
-                hasPicColumn = true;
-            }
-
-            if (hasPicColumn)
-            {
-                // 填充表头
-                var maxWidth = new int[properties.Length];
-                for (var j = 0; j < properties.Length; j++)
-                {
-                    sheet.SetValue(startRow, j + startColumn, properties[j].Name);
-                    maxWidth[j] = Encoding.UTF8.GetBytes(properties[j].Name).Length;
-                }
-
-                sheet.Row(startRow).Style.Font.Bold = true; // 表头设置为粗体
-                sheet.Row(startRow).Style.Font.Size = sheet.Row(startRow).Style.Font.Size * 1.11f; // 表头字号放大1.11倍
-                sheet.Row(startRow).Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
-                sheet.Row(startRow).CustomHeight = true; // 自动调整行高
-                if (settings != null)
-                {
-                    foreach (var x in settings.ColumnTypes)
-                    {
-                        sheet.Column(x.Key).Style.Numberformat.Format = x.Value;
-                    }
-                }
-                sheet.Cells.AutoFitColumns(); // 表头自适应列宽
-
-                // 填充内容
-                int current = 0;
-                foreach (var item in table)
-                {
-                    sheet.Row(current + startRow + 1).CustomHeight = true; // 自动调整行高
-                    for (int j = 0; j < properties.Length; j++)
-                    {
-                        switch (properties[j].GetValue(item))
-                        {
-                            case Stream s:
-                                {
-                                    if (s.Length > 2)
-                                    {
-                                        try
-                                        {
-                                            var picture = sheet.Drawings.AddPicture(Guid.NewGuid().ToString(), s);
-                                            picture.SetPosition(current + startRow, 3, j + startColumn - 1, 5); //设置图片显示位置
-                                            var percent = Math.Round(Math.Min(120f / picture.Image.Bounds.Height * picture.Image.Bounds.VerticalResolution, 100));
-                                            sheet.Row(current + startRow + 1).Height = 90;
-                                            sheet.Column(j + startColumn).Width = Math.Max(sheet.Column(j + startColumn).Width, picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent / 6.4);
-                                            picture.SetSize((int)percent);
-                                        }
-                                        catch
-                                        {
-                                            throw new ArgumentException($"{current + startRow}行{j}列图像格式不受支持");
-                                        }
-                                    }
-
-                                    sheet.SetValue(current + startRow + 1, j + startColumn, "");
-
-                                    break;
-                                }
-
-                            case IEnumerable<Stream> streams:
-                                {
-                                    double sumWidth = 0;
-                                    int index = 0;
-                                    foreach (var stream in streams.Where(stream => stream.Length > 2))
-                                    {
-                                        try
-                                        {
-                                            var picture = sheet.Drawings.AddPicture(Guid.NewGuid().ToString(), stream);
-                                            var percent = Math.Round(Math.Min(120f / picture.Image.Bounds.Height * picture.Image.Bounds.VerticalResolution, 100));
-                                            picture.SetPosition(current + startRow, 3, j + startColumn - 1, (int)Math.Ceiling(picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent * index++)); //设置图片显示位置
-                                            sheet.Row(current + startRow + 1).Height = 90;
-                                            sumWidth += picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent / 6.4;
-                                            sheet.Column(j + startColumn).Width = Math.Max(sheet.Column(j + startColumn).Width, sumWidth);
-                                            picture.SetSize((int)percent);
-                                        }
-                                        catch
-                                        {
-                                            throw new ArgumentException($"{current + startRow}行{j}列第{index}张图像格式不受支持");
-                                        }
-                                    }
-
-                                    sheet.SetValue(current + startRow + 1, j + startColumn, "");
-                                    break;
-                                }
-
-                            case IDictionary<string, Stream> dic:
-                                {
-                                    double sumWidth = 0;
-                                    int index = 0;
-                                    foreach (var kv in dic.Where(kv => kv.Value.Length > 2))
-                                    {
-                                        try
-                                        {
-                                            var picture = sheet.Drawings.AddPicture(Guid.NewGuid().ToString(), kv.Value, new Uri(kv.Key));
-                                            var percent = Math.Round(Math.Min(120f / picture.Image.Bounds.Height * picture.Image.Bounds.VerticalResolution, 100));
-                                            picture.SetPosition(current + startRow, 3, j + startColumn - 1, (int)Math.Ceiling(picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent * index++)); //设置图片显示位置
-                                            sheet.Row(current + startRow + 1).Height = 90;
-                                            sumWidth += picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent / 6.4;
-                                            sheet.Column(j + startColumn).Width = Math.Max(sheet.Column(j + startColumn).Width, sumWidth);
-                                            picture.SetSize((int)percent);
-                                        }
-                                        catch
-                                        {
-                                            throw new ArgumentException($"{current + startRow}行{j}列第{index}张图像格式不受支持,图片链接:{kv.Key}");
-                                        }
-                                    }
-
-                                    sheet.SetValue(current + startRow + 1, j + startColumn, "");
-                                    break;
-                                }
-
-                            case IDictionary<string, MemoryStream> dic:
-                                {
-                                    double sumWidth = 0;
-                                    int index = 0;
-                                    foreach (var kv in dic.Where(kv => kv.Value.Length > 2))
-                                    {
-                                        try
-                                        {
-                                            var picture = sheet.Drawings.AddPicture(Guid.NewGuid().ToString(), kv.Value, new Uri(kv.Key));
-                                            var percent = Math.Round(Math.Min(120f / picture.Image.Bounds.Height * picture.Image.Bounds.VerticalResolution, 100));
-                                            picture.SetPosition(current + startRow, 3, j + startColumn - 1, (int)Math.Ceiling(picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent * index++)); //设置图片显示位置
-                                            sheet.Row(current + startRow + 1).Height = 90;
-                                            sumWidth += picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent / 6.4;
-                                            sheet.Column(j + startColumn).Width = Math.Max(sheet.Column(j + startColumn).Width, sumWidth);
-                                            picture.SetSize((int)percent);
-                                        }
-                                        catch
-                                        {
-                                            throw new ArgumentException($"{current + startRow}行{j}列第{index}张图像格式不受支持,图片链接: {kv.Key}");
-                                        }
-                                    }
-
-                                    sheet.SetValue(current + startRow + 1, j + startColumn, "");
-                                    break;
-                                }
-
-                            case IDictionary<string, PooledMemoryStream> dic:
-                                {
-                                    double sumWidth = 0;
-                                    int index = 0;
-                                    foreach (var kv in dic.Where(kv => kv.Value.Length > 2))
-                                    {
-                                        try
-                                        {
-                                            var picture = sheet.Drawings.AddPicture(Guid.NewGuid().ToString(), kv.Value, new Uri(kv.Key));
-                                            var percent = Math.Round(Math.Min(120f / picture.Image.Bounds.Height * picture.Image.Bounds.VerticalResolution, 100));
-                                            picture.SetPosition(current + startRow, 3, j + startColumn - 1, (int)Math.Ceiling(picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent * index++)); //设置图片显示位置
-                                            sheet.Row(current + startRow + 1).Height = 90;
-                                            sumWidth += picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent / 6.4;
-                                            sheet.Column(j + startColumn).Width = Math.Max(sheet.Column(j + startColumn).Width, sumWidth);
-                                            picture.SetSize((int)percent);
-                                        }
-                                        catch
-                                        {
-                                            throw new ArgumentException($"{current + startRow}行{j}列第{index}张图像格式不受支持,图片链接: {kv.Key}");
-                                        }
-                                    }
-
-                                    sheet.SetValue(current + startRow + 1, j + startColumn, "");
-                                    break;
-                                }
-
-                            default:
-                                {
-                                    sheet.SetValue(current + startRow + 1, j + startColumn, properties[j].GetValue(item) ?? "");
-                                    if (properties[j].GetValue(item) is ValueType)
-                                    {
-                                        sheet.Column(j + startColumn).AutoFit();
-                                    }
-                                    else
-                                    {
-                                        // 根据单元格内容长度来自适应调整列宽
-                                        var fontFamily = SystemFonts.Families.FirstOrDefault(f => f.Name == sheet.Cells[current + startRow + 1, j + startColumn].Style.Font.Name);
-                                        int measureSize = 1;
-                                        if (fontFamily == default)
-                                        {
-                                            fontFamily = SystemFonts.Families.FirstOrDefault();
-                                            measureSize++;
-                                        }
-
-                                        var width = TextMeasurer.Measure(properties[j].GetValue(item).ToString(), new TextOptions(fontFamily.CreateFont(measureSize))).Width;
-                                        sheet.Column(j + startColumn).Width = Math.Min(110, Math.Max(width, sheet.Column(j + startColumn).Width));
-                                    }
-
-                                    break;
-                                }
-                        }
-                    }
-                    current++;
-                }
-
-                sheet.Cells.Style.WrapText = true;
-            }
-            else
-            {
-                sheet.Cells[startRow, startColumn].LoadFromCollection(table, true, TableStyles.Light15).AutoFitColumns(12, 90);
-                sheet.Cells.Style.WrapText = true;
-            }
-        }
-    }
-
-    private static readonly NumberFormater NumberFormater = new("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1);
-
-    /// <summary>
-    /// 获取字母列
-    /// </summary>
-    /// <param name="sheet"></param>
-    /// <param name="index"></param>
-    /// <returns></returns>
-    public static ExcelColumn Column(this ExcelWorksheet sheet, string index)
-    {
-        return sheet.Column((int)NumberFormater.FromString(index));
-    }
+	/// <summary>
+	/// 将内存表自动填充到Excel
+	/// </summary>
+	/// <param name="sheetTables">sheet名和内存表的映射</param>
+	/// <param name="password">密码</param>
+	/// <param name="settings">列设置</param>
+	/// <returns>内存流</returns>
+	public static PooledMemoryStream ToExcel(this Dictionary<string, DataTable> sheetTables, string password = null, ColumnSettings settings = null)
+	{
+		using var pkg = new ExcelPackage();
+		foreach (var pair in sheetTables)
+		{
+			pair.Value.TableName = pair.Key;
+			CreateWorksheet(pkg, pair.Value, settings);
+		}
+
+		return SaveAsStream(pkg, password);
+	}
+
+	/// <summary>
+	/// 将内存表自动填充到Excel
+	/// </summary>
+	/// <param name="sheetTables">sheet名和内存表的映射</param>
+	/// <param name="password">密码</param>
+	/// <param name="settings">列设置</param>
+	/// <returns>内存流</returns>
+	public static PooledMemoryStream ToExcel<T>(this Dictionary<string, IEnumerable<T>> sheetTables, string password = null, ColumnSettings settings = null)
+	{
+		using var pkg = new ExcelPackage();
+		foreach (var pair in sheetTables)
+		{
+			CreateWorksheet(pkg, pair, settings);
+		}
+
+		return SaveAsStream(pkg, password);
+	}
+
+	/// <summary>
+	/// 将内存表自动填充到Excel
+	/// </summary>
+	/// <param name="tables">内存表</param>
+	/// <param name="password">密码</param>
+	/// <returns>内存流</returns>
+	public static PooledMemoryStream ToExcel(this List<DataTable> tables, string password = null, ColumnSettings settings = null)
+	{
+		using var pkg = new ExcelPackage();
+		foreach (var table in tables)
+		{
+			CreateWorksheet(pkg, table, settings);
+		}
+
+		return SaveAsStream(pkg, password);
+	}
+
+	/// <summary>
+	/// 将内存表自动填充到Excel
+	/// </summary>
+	/// <param name="table">内存表</param>
+	/// <param name="password">密码</param>
+	/// <returns>内存流</returns>
+	public static PooledMemoryStream ToExcel(this DataTable table, string password = null, ColumnSettings settings = null)
+	{
+		using var pkg = new ExcelPackage();
+		CreateWorksheet(pkg, table, settings);
+		return SaveAsStream(pkg, password);
+	}
+
+	/// <summary>
+	/// 将内存表自动填充到Excel
+	/// </summary>
+	/// <param name="table">内存表</param>
+	/// <param name="password">密码</param>
+	/// <returns>内存流</returns>
+	public static PooledMemoryStream ToExcel<T>(this IEnumerable<T> table, string password = null, ColumnSettings settings = null)
+	{
+		using var pkg = new ExcelPackage();
+		CreateWorksheet(pkg, new KeyValuePair<string, IEnumerable<T>>("Sheet1", table), settings);
+		return SaveAsStream(pkg, password);
+	}
+
+	private static PooledMemoryStream SaveAsStream(ExcelPackage pkg, string password)
+	{
+		var ms = new PooledMemoryStream();
+		if (!string.IsNullOrEmpty(password))
+		{
+			pkg.SaveAs(ms, password);
+		}
+		else
+		{
+			pkg.SaveAs(ms);
+		}
+
+		ms.Position = 0;
+		return ms;
+	}
+
+	public static void CreateWorksheet(this ExcelPackage pkg, DataTable table, ColumnSettings settings = null)
+	{
+		if (string.IsNullOrEmpty(table.TableName))
+		{
+			table.TableName = "Sheet1";
+		}
+
+		var sheet = pkg.Workbook.Worksheets.Add(table.TableName);
+		sheet.Cells.Style.Font.Name = "微软雅黑";
+
+		FillWorksheet(sheet, table, settings);
+
+		sheet.Cells.Style.VerticalAlignment = ExcelVerticalAlignment.Center;
+
+		//打印方向:纵向
+		sheet.PrinterSettings.Orientation = eOrientation.Landscape;
+
+		//集中在一页里打印
+		sheet.PrinterSettings.FitToPage = true;
+
+		//使用A4纸
+		sheet.PrinterSettings.PaperSize = ePaperSize.A4;
+	}
+
+	public static void CreateWorksheet<T>(this ExcelPackage pkg, KeyValuePair<string, IEnumerable<T>> table, ColumnSettings settings = null)
+	{
+		var sheet = pkg.Workbook.Worksheets.Add(table.Key);
+		sheet.Cells.Style.Font.Name = "微软雅黑";
+
+		FillWorksheet(sheet, table.Value, settings);
+
+		sheet.Cells.Style.VerticalAlignment = ExcelVerticalAlignment.Center;
+
+		//打印方向:纵向
+		sheet.PrinterSettings.Orientation = eOrientation.Landscape;
+
+		//集中在一页里打印
+		sheet.PrinterSettings.FitToPage = true;
+
+		//使用A4纸
+		sheet.PrinterSettings.PaperSize = ePaperSize.A4;
+	}
+
+	/// <summary>
+	/// 从datatable填充工作簿
+	/// </summary>
+	/// <param name="sheet">工作簿</param>
+	/// <param name="table">数据</param>
+	/// <param name="settings">列设置</param>
+	/// <param name="startRow">起始行,默认第一行</param>
+	/// <param name="startColumn">起始列,默认第一列A列</param>
+	public static void FillWorksheet(this ExcelWorksheet sheet, DataTable table, ColumnSettings settings = null, int startRow = 1, int startColumn = 1)
+	{
+		var hasPicColumn = false;
+		if (table.Rows.Count > 0)
+		{
+			for (int i = 0; i < table.Columns.Count; i++)
+			{
+				switch (table.Rows[0][i])
+				{
+					case Stream:
+					case IEnumerable<Stream>:
+					case IDictionary<string, Stream>:
+					case IDictionary<string, MemoryStream>:
+						hasPicColumn = true;
+						break;
+				}
+			}
+
+			if (hasPicColumn)
+			{
+				// 填充表头
+				var maxWidth = new int[table.Columns.Count];
+				for (var j = 0; j < table.Columns.Count; j++)
+				{
+					sheet.SetValue(startRow, j + startColumn, table.Columns[j].ColumnName);
+					maxWidth[j] = Encoding.UTF8.GetBytes(table.Columns[j].ColumnName).Length;
+				}
+
+				sheet.Row(startRow).Style.Font.Bold = true; // 表头设置为粗体
+				sheet.Row(startRow).Style.Font.Size = sheet.Row(startRow).Style.Font.Size * 1.11f; // 表头字号放大1.11倍
+				sheet.Row(startRow).Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
+				sheet.Row(startRow).CustomHeight = true; // 自动调整行高
+				if (settings != null)
+				{
+					foreach (var x in settings.ColumnTypes)
+					{
+						sheet.Column(x.Key).Style.Numberformat.Format = x.Value;
+					}
+				}
+				sheet.Cells.AutoFitColumns(); // 表头自适应列宽
+
+				// 填充内容
+				for (int i = 0; i < table.Rows.Count; i++)
+				{
+					sheet.Row(i + startRow + 1).CustomHeight = true; // 自动调整行高
+					for (int j = 0; j < table.Columns.Count; j++)
+					{
+						switch (table.Rows[i][j])
+						{
+							case Stream s:
+								{
+									if (s.Length > 2)
+									{
+										try
+										{
+											var picture = sheet.Drawings.AddPicture(Guid.NewGuid().ToString(), s);
+											picture.SetPosition(i + startRow, 3, j + startColumn - 1, 5); //设置图片显示位置
+											var percent = Math.Round(Math.Min(120f / picture.Image.Bounds.Height * picture.Image.Bounds.VerticalResolution, 100));
+											sheet.Row(i + startRow + 1).Height = 90;
+											sheet.Column(j + startColumn).Width = Math.Max(sheet.Column(j + startColumn).Width, picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent / 6.4);
+											picture.SetSize((int)percent);
+										}
+										catch
+										{
+											throw new ArgumentException($"{i + startRow}行{j}列图像格式不受支持");
+										}
+									}
+
+									sheet.SetValue(i + startRow + 1, j + startColumn, "");
+
+									break;
+								}
+
+							case IEnumerable<Stream> streams:
+								{
+									double sumWidth = 0;
+									int index = 0;
+									foreach (var stream in streams.Where(stream => stream.Length > 2))
+									{
+										try
+										{
+											var picture = sheet.Drawings.AddPicture(Guid.NewGuid().ToString(), stream);
+											var percent = Math.Round(Math.Min(120f / picture.Image.Bounds.Height * picture.Image.Bounds.VerticalResolution, 100));
+											picture.SetPosition(i + startRow, 3, j + startColumn - 1, (int)Math.Ceiling(picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent * index++)); //设置图片显示位置
+											sheet.Row(i + startRow + 1).Height = 90;
+											sumWidth += picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent / 6.4;
+											sheet.Column(j + startColumn).Width = Math.Max(sheet.Column(j + startColumn).Width, sumWidth);
+											picture.SetSize((int)percent);
+										}
+										catch
+										{
+											throw new ArgumentException($"{i + startRow}行{j}列第{index}张图像格式不受支持");
+										}
+									}
+
+									sheet.SetValue(i + startRow + 1, j + startColumn, "");
+									break;
+								}
+
+							case IDictionary<string, Stream> dic:
+								{
+									double sumWidth = 0;
+									int index = 0;
+									foreach (var kv in dic.Where(kv => kv.Value.Length > 2))
+									{
+										try
+										{
+											var picture = sheet.Drawings.AddPicture(Guid.NewGuid().ToString(), kv.Value, new Uri(kv.Key));
+											var percent = Math.Round(Math.Min(120f / picture.Image.Bounds.Height * picture.Image.Bounds.VerticalResolution, 100));
+											picture.SetPosition(i + startRow, 3, j + startColumn - 1, (int)Math.Ceiling(picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent * index++)); //设置图片显示位置
+											sheet.Row(i + startRow + 1).Height = 90;
+											sumWidth += picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent / 6.4;
+											sheet.Column(j + startColumn).Width = Math.Max(sheet.Column(j + startColumn).Width, sumWidth);
+											picture.SetSize((int)percent);
+										}
+										catch
+										{
+											throw new ArgumentException($"{i + startRow}行{j}列第{index}张图像格式不受支持,图片链接:{kv.Key}");
+										}
+									}
+
+									sheet.SetValue(i + startRow + 1, j + startColumn, "");
+									break;
+								}
+
+							case IDictionary<string, MemoryStream> dic:
+								{
+									double sumWidth = 0;
+									int index = 0;
+									foreach (var kv in dic.Where(kv => kv.Value.Length > 2))
+									{
+										try
+										{
+											var picture = sheet.Drawings.AddPicture(Guid.NewGuid().ToString(), kv.Value, new Uri(kv.Key));
+											var percent = Math.Round(Math.Min(120f / picture.Image.Bounds.Height * picture.Image.Bounds.VerticalResolution, 100));
+											picture.SetPosition(i + startRow, 3, j + startColumn - 1, (int)Math.Ceiling(picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent * index++)); //设置图片显示位置
+											sheet.Row(i + startRow + 1).Height = 90;
+											sumWidth += picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent / 6.4;
+											sheet.Column(j + startColumn).Width = Math.Max(sheet.Column(j + startColumn).Width, sumWidth);
+											picture.SetSize((int)percent);
+										}
+										catch
+										{
+											throw new ArgumentException($"{i + startRow}行{j}列第{index}张图像格式不受支持,图片链接:{kv.Key}");
+										}
+									}
+
+									sheet.SetValue(i + startRow + 1, j + startColumn, "");
+									break;
+								}
+
+							case IDictionary<string, PooledMemoryStream> dic:
+								{
+									double sumWidth = 0;
+									int index = 0;
+									foreach (var kv in dic.Where(kv => kv.Value.Length > 2))
+									{
+										try
+										{
+											var picture = sheet.Drawings.AddPicture(Guid.NewGuid().ToString(), kv.Value, new Uri(kv.Key));
+											var percent = Math.Round(Math.Min(120f / picture.Image.Bounds.Height * picture.Image.Bounds.VerticalResolution, 100));
+											picture.SetPosition(i + startRow, 3, j + startColumn - 1, (int)Math.Ceiling(picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent * index++)); //设置图片显示位置
+											sheet.Row(i + startRow + 1).Height = 90;
+											sumWidth += picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent / 6.4;
+											sheet.Column(j + startColumn).Width = Math.Max(sheet.Column(j + startColumn).Width, sumWidth);
+											picture.SetSize((int)percent);
+										}
+										catch
+										{
+											throw new ArgumentException($"{i + startRow}行{j}列第{index}张图像格式不受支持,图片链接:{kv.Key}");
+										}
+									}
+
+									sheet.SetValue(i + startRow + 1, j + startColumn, "");
+									break;
+								}
+
+							default:
+								{
+									sheet.SetValue(i + startRow + 1, j + startColumn, table.Rows[i][j] ?? "");
+									if (table.Rows[i][j] is ValueType)
+									{
+										sheet.Column(j + startColumn).AutoFit();
+									}
+									else
+									{
+										// 根据单元格内容长度来自适应调整列宽
+										var fontFamily = SystemFonts.Families.FirstOrDefault(f => f.Name == sheet.Cells[i + startRow + 1, j + startColumn].Style.Font.Name);
+										int measureSize = 1;
+										if (fontFamily == default)
+										{
+											fontFamily = SystemFonts.Families.FirstOrDefault();
+											measureSize++;
+										}
+
+										var width = TextMeasurer.MeasureSize(table.Rows[i][j].ToString(), new TextOptions(fontFamily.CreateFont(measureSize))).Width;
+										sheet.Column(j + startColumn).Width = Math.Min(110, Math.Max(width, sheet.Column(j + startColumn).Width));
+									}
+
+									break;
+								}
+						}
+					}
+				}
+
+				sheet.Cells.Style.WrapText = true;
+			}
+			else
+			{
+				sheet.Cells[startRow, startColumn].LoadFromDataTable(table, true, TableStyles.Light15).AutoFitColumns(12, 90);
+				sheet.Cells.Style.WrapText = true;
+			}
+		}
+	}
+
+	/// <summary>
+	/// 从datatable填充工作簿
+	/// </summary>
+	/// <param name="sheet">工作簿</param>
+	/// <param name="table">数据</param>
+	/// <param name="settings">列设置</param>
+	/// <param name="startRow">起始行,默认第一行</param>
+	/// <param name="startColumn">起始列,默认第一列A列</param>
+	public static void FillWorksheet<T>(this ExcelWorksheet sheet, IEnumerable<T> source, ColumnSettings settings = null, int startRow = 1, int startColumn = 1)
+	{
+		var hasPicColumn = false;
+		var properties = typeof(T).GetProperties();
+		if (source is ICollection<T> table)
+		{
+		}
+		else
+		{
+			table = source.ToList();
+		}
+
+		if (table.Any())
+		{
+			if (properties.Any(t => t.PropertyType.IsSubclassOf(typeof(Stream)) || typeof(IEnumerable<Stream>).IsAssignableFrom(t.PropertyType) || (typeof(IDictionary).IsAssignableFrom(t.PropertyType) && t.PropertyType.GenericTypeArguments[1].IsSubclassOf(typeof(Stream)))))
+			{
+				hasPicColumn = true;
+			}
+
+			if (hasPicColumn)
+			{
+				// 填充表头
+				var maxWidth = new int[properties.Length];
+				for (var j = 0; j < properties.Length; j++)
+				{
+					sheet.SetValue(startRow, j + startColumn, properties[j].Name);
+					maxWidth[j] = Encoding.UTF8.GetBytes(properties[j].Name).Length;
+				}
+
+				sheet.Row(startRow).Style.Font.Bold = true; // 表头设置为粗体
+				sheet.Row(startRow).Style.Font.Size = sheet.Row(startRow).Style.Font.Size * 1.11f; // 表头字号放大1.11倍
+				sheet.Row(startRow).Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
+				sheet.Row(startRow).CustomHeight = true; // 自动调整行高
+				if (settings != null)
+				{
+					foreach (var x in settings.ColumnTypes)
+					{
+						sheet.Column(x.Key).Style.Numberformat.Format = x.Value;
+					}
+				}
+				sheet.Cells.AutoFitColumns(); // 表头自适应列宽
+
+				// 填充内容
+				int current = 0;
+				foreach (var item in table)
+				{
+					sheet.Row(current + startRow + 1).CustomHeight = true; // 自动调整行高
+					for (int j = 0; j < properties.Length; j++)
+					{
+						switch (properties[j].GetValue(item))
+						{
+							case Stream s:
+								{
+									if (s.Length > 2)
+									{
+										try
+										{
+											var picture = sheet.Drawings.AddPicture(Guid.NewGuid().ToString(), s);
+											picture.SetPosition(current + startRow, 3, j + startColumn - 1, 5); //设置图片显示位置
+											var percent = Math.Round(Math.Min(120f / picture.Image.Bounds.Height * picture.Image.Bounds.VerticalResolution, 100));
+											sheet.Row(current + startRow + 1).Height = 90;
+											sheet.Column(j + startColumn).Width = Math.Max(sheet.Column(j + startColumn).Width, picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent / 6.4);
+											picture.SetSize((int)percent);
+										}
+										catch
+										{
+											throw new ArgumentException($"{current + startRow}行{j}列图像格式不受支持");
+										}
+									}
+
+									sheet.SetValue(current + startRow + 1, j + startColumn, "");
+
+									break;
+								}
+
+							case IEnumerable<Stream> streams:
+								{
+									double sumWidth = 0;
+									int index = 0;
+									foreach (var stream in streams.Where(stream => stream.Length > 2))
+									{
+										try
+										{
+											var picture = sheet.Drawings.AddPicture(Guid.NewGuid().ToString(), stream);
+											var percent = Math.Round(Math.Min(120f / picture.Image.Bounds.Height * picture.Image.Bounds.VerticalResolution, 100));
+											picture.SetPosition(current + startRow, 3, j + startColumn - 1, (int)Math.Ceiling(picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent * index++)); //设置图片显示位置
+											sheet.Row(current + startRow + 1).Height = 90;
+											sumWidth += picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent / 6.4;
+											sheet.Column(j + startColumn).Width = Math.Max(sheet.Column(j + startColumn).Width, sumWidth);
+											picture.SetSize((int)percent);
+										}
+										catch
+										{
+											throw new ArgumentException($"{current + startRow}行{j}列第{index}张图像格式不受支持");
+										}
+									}
+
+									sheet.SetValue(current + startRow + 1, j + startColumn, "");
+									break;
+								}
+
+							case IDictionary<string, Stream> dic:
+								{
+									double sumWidth = 0;
+									int index = 0;
+									foreach (var kv in dic.Where(kv => kv.Value.Length > 2))
+									{
+										try
+										{
+											var picture = sheet.Drawings.AddPicture(Guid.NewGuid().ToString(), kv.Value, new Uri(kv.Key));
+											var percent = Math.Round(Math.Min(120f / picture.Image.Bounds.Height * picture.Image.Bounds.VerticalResolution, 100));
+											picture.SetPosition(current + startRow, 3, j + startColumn - 1, (int)Math.Ceiling(picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent * index++)); //设置图片显示位置
+											sheet.Row(current + startRow + 1).Height = 90;
+											sumWidth += picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent / 6.4;
+											sheet.Column(j + startColumn).Width = Math.Max(sheet.Column(j + startColumn).Width, sumWidth);
+											picture.SetSize((int)percent);
+										}
+										catch
+										{
+											throw new ArgumentException($"{current + startRow}行{j}列第{index}张图像格式不受支持,图片链接:{kv.Key}");
+										}
+									}
+
+									sheet.SetValue(current + startRow + 1, j + startColumn, "");
+									break;
+								}
+
+							case IDictionary<string, MemoryStream> dic:
+								{
+									double sumWidth = 0;
+									int index = 0;
+									foreach (var kv in dic.Where(kv => kv.Value.Length > 2))
+									{
+										try
+										{
+											var picture = sheet.Drawings.AddPicture(Guid.NewGuid().ToString(), kv.Value, new Uri(kv.Key));
+											var percent = Math.Round(Math.Min(120f / picture.Image.Bounds.Height * picture.Image.Bounds.VerticalResolution, 100));
+											picture.SetPosition(current + startRow, 3, j + startColumn - 1, (int)Math.Ceiling(picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent * index++)); //设置图片显示位置
+											sheet.Row(current + startRow + 1).Height = 90;
+											sumWidth += picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent / 6.4;
+											sheet.Column(j + startColumn).Width = Math.Max(sheet.Column(j + startColumn).Width, sumWidth);
+											picture.SetSize((int)percent);
+										}
+										catch
+										{
+											throw new ArgumentException($"{current + startRow}行{j}列第{index}张图像格式不受支持,图片链接: {kv.Key}");
+										}
+									}
+
+									sheet.SetValue(current + startRow + 1, j + startColumn, "");
+									break;
+								}
+
+							case IDictionary<string, PooledMemoryStream> dic:
+								{
+									double sumWidth = 0;
+									int index = 0;
+									foreach (var kv in dic.Where(kv => kv.Value.Length > 2))
+									{
+										try
+										{
+											var picture = sheet.Drawings.AddPicture(Guid.NewGuid().ToString(), kv.Value, new Uri(kv.Key));
+											var percent = Math.Round(Math.Min(120f / picture.Image.Bounds.Height * picture.Image.Bounds.VerticalResolution, 100));
+											picture.SetPosition(current + startRow, 3, j + startColumn - 1, (int)Math.Ceiling(picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent * index++)); //设置图片显示位置
+											sheet.Row(current + startRow + 1).Height = 90;
+											sumWidth += picture.Image.Bounds.Width / picture.Image.Bounds.HorizontalResolution * percent / 6.4;
+											sheet.Column(j + startColumn).Width = Math.Max(sheet.Column(j + startColumn).Width, sumWidth);
+											picture.SetSize((int)percent);
+										}
+										catch
+										{
+											throw new ArgumentException($"{current + startRow}行{j}列第{index}张图像格式不受支持,图片链接: {kv.Key}");
+										}
+									}
+
+									sheet.SetValue(current + startRow + 1, j + startColumn, "");
+									break;
+								}
+
+							default:
+								{
+									sheet.SetValue(current + startRow + 1, j + startColumn, properties[j].GetValue(item) ?? "");
+									if (properties[j].GetValue(item) is ValueType)
+									{
+										sheet.Column(j + startColumn).AutoFit();
+									}
+									else
+									{
+										// 根据单元格内容长度来自适应调整列宽
+										var fontFamily = SystemFonts.Families.FirstOrDefault(f => f.Name == sheet.Cells[current + startRow + 1, j + startColumn].Style.Font.Name);
+										int measureSize = 1;
+										if (fontFamily == default)
+										{
+											fontFamily = SystemFonts.Families.FirstOrDefault();
+											measureSize++;
+										}
+
+										var width = TextMeasurer.MeasureSize(properties[j].GetValue(item).ToString(), new TextOptions(fontFamily.CreateFont(measureSize))).Width;
+										sheet.Column(j + startColumn).Width = Math.Min(110, Math.Max(width, sheet.Column(j + startColumn).Width));
+									}
+
+									break;
+								}
+						}
+					}
+					current++;
+				}
+
+				sheet.Cells.Style.WrapText = true;
+			}
+			else
+			{
+				sheet.Cells[startRow, startColumn].LoadFromCollection(table, true, TableStyles.Light15).AutoFitColumns(12, 90);
+				sheet.Cells.Style.WrapText = true;
+			}
+		}
+	}
+
+	private static readonly NumberFormater NumberFormater = new("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1);
+
+	/// <summary>
+	/// 获取字母列
+	/// </summary>
+	/// <param name="sheet"></param>
+	/// <param name="index"></param>
+	/// <returns></returns>
+	public static ExcelColumn Column(this ExcelWorksheet sheet, string index)
+	{
+		return sheet.Column((int)NumberFormater.FromString(index));
+	}
 }

+ 2 - 2
Masuit.Tools.Excel/Masuit.Tools.Excel.csproj

@@ -3,7 +3,7 @@
         <TargetFramework>netstandard2.0</TargetFramework>
         <LangVersion>latest</LangVersion>
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-        <Version>1.2.6.2</Version>
+        <Version>1.2.7</Version>
         <Authors>懒得勤快</Authors>
         <Description>Masuit.Tools.Excel导出库,支持一些简单数据的导出,支持图片列</Description>
         <Copyright>懒得勤快</Copyright>
@@ -16,7 +16,7 @@
         <RepositoryType>Github</RepositoryType>
         <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
         <PackageRequireLicenseAcceptance>False</PackageRequireLicenseAcceptance>
-        <FileVersion>1.2.4.2</FileVersion>
+        <FileVersion>1.2.5</FileVersion>
         <Company>masuit.org</Company>
         <AssemblyVersion>1.2.3</AssemblyVersion>
         <PackageLicenseUrl>https://github.com/ldqk/Masuit.Tools/blob/master/LICENSE</PackageLicenseUrl>

+ 1 - 1
Masuit.Tools.Net45/package.nuspec

@@ -2,7 +2,7 @@
 <package>
   <metadata>
     <id>Masuit.Tools.Net45</id>
-    <version>2.6.4.1</version>
+    <version>2.6.5</version>
     <title>Masuit.Tools</title>
     <authors>懒得勤快</authors>
     <owners>masuit.com</owners>

+ 4 - 1
Masuit.Tools/Masuit.Tools.csproj

@@ -130,6 +130,9 @@
     <Compile Include="..\Masuit.Tools.Abstractions\Win32\*.*">
       <Link>Win32\%(RecursiveDir)%(FileName)%(Extension)</Link>
     </Compile>
+    <Compile Include="..\Masuit.Tools.Abstractions\Win32\*\*.*">
+      <Link>Win32\%(RecursiveDir)%(FileName)%(Extension)</Link>
+    </Compile>
     <Compile Include="Config\CoreConfig.cs" />
     <Compile Include="Files\SevenZipCompressor.cs" />
     <Compile Include="Files\ExtensionAttach.cs" />
@@ -196,7 +199,7 @@
       <Version>[2.1.3]</Version>
     </PackageReference>
     <PackageReference Include="SixLabors.ImageSharp.Drawing">
-      <Version>1.0.0-beta15</Version>
+      <Version>1.0.0</Version>
     </PackageReference>
     <PackageReference Include="StackExchange.Redis">
       <Version>2.6.122</Version>

+ 1 - 1
Masuit.Tools/package.nuspec

@@ -2,7 +2,7 @@
 <package>
   <metadata>
     <id>Masuit.Tools.Net</id>
-    <version>2.6.4.1</version>
+    <version>2.6.5</version>
     <title>Masuit.Tools</title>
     <authors>懒得勤快</authors>
     <owners>masuit.com</owners>

+ 18 - 0
README.md

@@ -1257,6 +1257,24 @@ detector.FormatCategories;//格式类别
         Assert.Equal(obj.Name, obj["Name"]);
         Assert.Equal(obj["MyClass"]["X"], obj.MyClass.X);
 ```
+### 46. 反病毒
+```csharp
+// 要求系统WindowsDefender没有被精简掉
+var windowsDefender = new WindowsDefenderScanService();
+var result = windowsDefender.ScanFile(@"Y:\1.exe"); // 扫描文件
+var result = windowsDefender.ScanDirectory(@"Y:\"); // 扫描文件夹
+var result = windowsDefender.ScanStream(stream); // 扫描文件流
+
+// 要求C:\Windows\System32\amsi.dll文件存在
+var amsiService = new AmsiScanService();
+amsiService.Scan(stream); // 扫描文件流
+amsiService.Scan(@"Y:\1.exe"); // 扫描文件
+amsiService.Scan(bytes); // 扫描二进制数组
+
+// ASP.NET Core
+service.AddWindowsDefenderService();
+service.AddAMSI();
+```
 
 # Asp.Net MVC和Asp.Net Core的支持断点续传和多线程下载的ResumeFileResult
 

+ 1 - 1
Test/Masuit.Tools.Test/Masuit.Tools.Test.csproj

@@ -94,7 +94,7 @@
       <Version>2.0.0</Version>
     </PackageReference>
     <PackageReference Include="Moq">
-      <Version>4.20.2</Version>
+      <Version>4.20.69</Version>
     </PackageReference>
     <PackageReference Include="MSTest.TestAdapter">
       <Version>3.1.1</Version>