众所周知使用System.Drawing.Imaging是无法在Linux环境生成图片的
因为Linux环境没有相关组件且会报错
The type initializer for 'Gdip' threw an exception
换了一个库SkiaSharp还找来一个帮助类
using SkiaSharp;
using System.Text;
namespace Common.Helper
{
/// <summary>
/// 跨平台验证码生成类
/// https://github.com/mono/SkiaSharp/issues/1341
/// </summary>
public class ValidateCodeHelper
{
private Random objRandom = new Random();
#region setting
/// <summary>
/// //验证码长度
/// </summary>
public int SetLength { get; set; } = 4;
/// <summary>
/// 验证码字符串
/// </summary>
public string SetVerifyCodeText { get; set; }
/// <summary>
/// 是否加入小写字母
/// </summary>
public bool SetAddLowerLetter { get; set; } = true;
/// <summary>
/// 是否加入大写字母
/// </summary>
public bool SetAddUpperLetter { get; set; } = true;
/// <summary>
/// 字体大小
/// </summary>
public int SetFontSize = 36;
/// <summary>
/// //字体颜色
/// </summary>
public SKColor SetFontColor { get; set; } = SKColors.Blue;
/// <summary>
/// 字体类型
/// </summary>
public string SetFontFamily = "Verdana";
/// <summary>
/// 背景色
/// </summary>
public SKColor SetBackgroundColor { get; set; } = SKColors.AliceBlue;
/// <summary>
/// 是否加入背景线
/// </summary>
public bool SetIsBackgroundLine { get; set; }
/// <summary>
/// 前景噪点数量
/// </summary>
public int SetForeNoisePointCount { get; set; } = 2;
/// <summary>
/// 随机码的旋转角度
/// </summary>
public int SetRandomAngle { get; set; } = 40;
/// <summary>
/// 是否随机字体颜色
/// </summary>
public bool SetIsRandomColor { get; set; } = true;
/// <summary>
/// 图片宽度
/// </summary>
public int SetWith { get; set; } = 200;
/// <summary>
/// 图片高度
/// </summary>
public int SetHeight { get; set; } = 40;
/// <summary>
/// 问题验证码答案,适用于运算符
/// </summary>
public string VerifyCodeResult { get; private set; }
#endregion
#region Constructor Method
/// <summary>
/// ValidateCodeHelper
/// </summary>
/// <param name="length"></param>
/// <param name="isOperation"></param>
public ValidateCodeHelper(int length = 4, bool isOperation = false)
{
if (isOperation)
{
var dic = GetQuestion();
SetVerifyCodeText = dic.Key;
VerifyCodeResult = dic.Value;
SetRandomAngle = 0;
}
else
{
SetLength = length;
GetVerifyCodeText();
}
SetWith = SetVerifyCodeText.Length * SetFontSize;
SetHeight = Convert.ToInt32((60.0 / 100) * SetFontSize + SetFontSize);
InitColors();
}
#endregion
#region Private Method
/// <summary>
/// 得到验证码字符串
/// </summary>
private void GetVerifyCodeText()
{
// 没有外部输入验证码时随机生成
if (string.IsNullOrEmpty(SetVerifyCodeText))
{
StringBuilder objStringBuilder = new StringBuilder();
// 加入数字1-9
for (int i = 1; i <= 9; i++)
{
objStringBuilder.Append(i.ToString());
}
// 加入大写字母A-Z,不包括O
if (SetAddUpperLetter)
{
char temp = ' ';
for (int i = 0; i < 26; i++)
{
temp = Convert.ToChar(i + 65);
// 如果生成的字母不是'O'
if (!temp.Equals('O'))
{
objStringBuilder.Append(temp);
}
}
}
// 加入小写字母a-z,不包括o
if (SetAddLowerLetter)
{
char temp = ' ';
for (int i = 0; i < 26; i++)
{
temp = Convert.ToChar(i + 97);
// 如果生成的字母不是'o'
if (!temp.Equals('o'))
{
objStringBuilder.Append(temp);
}
}
}
// 生成验证码字符串
{
int index = 0;
for (int i = 0; i < SetLength; i++)
{
index = objRandom.Next(0, objStringBuilder.Length);
SetVerifyCodeText += objStringBuilder[index];
objStringBuilder.Remove(index, 1);
}
}
}
}
/// <summary>
/// 获取随机颜色
/// </summary>
/// <returns></returns>
private SKColor GetRandomColor()
{
Random RandomNum_First = new Random((int)DateTime.Now.Ticks);
// 对于C#的随机数,没什么好说的
System.Threading.Thread.Sleep(RandomNum_First.Next(50));
Random RandomNum_Sencond = new Random((int)DateTime.Now.Ticks)
// 为了在白色背景上显示,尽量生成深色
int int_Red = RandomNum_First.Next(256);
int int_Green = RandomNum_Sencond.Next(256);
int int_Blue = (int_Red + int_Green > 400) ? 0 : 400 - int_Red - int_Green;
int_Blue = (int_Blue > 255) ? 255 : int_Blue;
return SKColor.FromHsv(int_Red, int_Green, int_Blue);
}
#endregion
#region Public Method
/// <summary>
/// 获取问题
/// </summary>
/// <param name="questionList">默认数字加减验证</param>
/// <returns></returns>
public KeyValuePair<string, string> GetQuestion(Dictionary<string, string> questionList = null)
{
if (questionList == null)
{
questionList = new Dictionary<string, string>();
var operArray = new string[] { "+", "*", "-", "/" };
var left = objRandom.Next(0, 10);
var right = objRandom.Next(0, 10);
var oper = operArray[objRandom.Next(0, operArray.Length)];
string key = string.Empty, val = string.Empty;
switch (oper)
{
case "+":
key = string.Format("{0}+{1}=?", left, right);
val = (left + right).ToString();
questionList.Add(key, val);
break;
case "*":
key = string.Format("{0}×{1}=?", left, right);
val = (left * right).ToString();
questionList.Add(key, val);
break;
case "-":
if (left < right)
{
var intTemp = left;
left = right;
right = intTemp;
}
questionList.Add(left + "-" + right + "= ?", (left - right).ToString());
break;
case "/":
right = objRandom.Next(1, 10);
left = right * objRandom.Next(1, 10);
questionList.Add(left + "÷" + right + "= ?", (left / right).ToString());
break;
}
}
return questionList.ToList()[objRandom.Next(0, questionList.Count)];
}
#endregion
#region newCode
/// <summary>
/// 干扰线的颜色集合
/// </summary>
private List<SKColor> colors { get; set; }
/// <summary>
/// InitColors
/// </summary>
public void InitColors()
{
colors = new List<SKColor>();
colors.Add(SKColors.AliceBlue);
colors.Add(SKColors.PaleGreen);
colors.Add(SKColors.PaleGoldenrod);
colors.Add(SKColors.Orchid);
colors.Add(SKColors.OrangeRed);
colors.Add(SKColors.Orange);
colors.Add(SKColors.OliveDrab);
colors.Add(SKColors.Olive);
colors.Add(SKColors.OldLace);
colors.Add(SKColors.Navy);
colors.Add(SKColors.NavajoWhite);
colors.Add(SKColors.Moccasin);
colors.Add(SKColors.MistyRose);
colors.Add(SKColors.MintCream);
colors.Add(SKColors.MidnightBlue);
colors.Add(SKColors.MediumVioletRed);
colors.Add(SKColors.MediumTurquoise);
colors.Add(SKColors.MediumSpringGreen);
colors.Add(SKColors.LightSlateGray);
colors.Add(SKColors.LightSteelBlue);
colors.Add(SKColors.LightYellow);
colors.Add(SKColors.Lime);
colors.Add(SKColors.LimeGreen);
colors.Add(SKColors.Linen);
colors.Add(SKColors.PaleTurquoise);
colors.Add(SKColors.Magenta);
colors.Add(SKColors.MediumAquamarine);
colors.Add(SKColors.MediumBlue);
colors.Add(SKColors.MediumOrchid);
colors.Add(SKColors.MediumPurple);
colors.Add(SKColors.MediumSeaGreen);
colors.Add(SKColors.MediumSlateBlue);
colors.Add(SKColors.Maroon);
colors.Add(SKColors.PaleVioletRed);
colors.Add(SKColors.PapayaWhip);
colors.Add(SKColors.PeachPuff);
colors.Add(SKColors.Snow);
colors.Add(SKColors.SpringGreen);
colors.Add(SKColors.SteelBlue);
colors.Add(SKColors.Tan);
colors.Add(SKColors.Teal);
colors.Add(SKColors.Thistle);
colors.Add(SKColors.SlateGray);
colors.Add(SKColors.Tomato);
colors.Add(SKColors.Violet);
colors.Add(SKColors.Wheat);
colors.Add(SKColors.White);
colors.Add(SKColors.WhiteSmoke);
colors.Add(SKColors.Yellow);
colors.Add(SKColors.YellowGreen);
colors.Add(SKColors.Turquoise);
colors.Add(SKColors.LightSkyBlue);
colors.Add(SKColors.SlateBlue);
colors.Add(SKColors.Silver);
colors.Add(SKColors.Peru);
colors.Add(SKColors.Pink);
colors.Add(SKColors.Plum);
colors.Add(SKColors.PowderBlue);
colors.Add(SKColors.Purple);
colors.Add(SKColors.Red);
colors.Add(SKColors.SkyBlue);
colors.Add(SKColors.RosyBrown);
colors.Add(SKColors.SaddleBrown);
colors.Add(SKColors.Salmon);
colors.Add(SKColors.SandyBrown);
colors.Add(SKColors.SeaGreen);
colors.Add(SKColors.SeaShell);
colors.Add(SKColors.Sienna);
colors.Add(SKColors.RoyalBlue);
colors.Add(SKColors.LightSeaGreen);
colors.Add(SKColors.LightSalmon);
colors.Add(SKColors.LightPink);
colors.Add(SKColors.Crimson);
colors.Add(SKColors.Cyan);
colors.Add(SKColors.DarkBlue);
colors.Add(SKColors.DarkCyan);
colors.Add(SKColors.DarkGoldenrod);
colors.Add(SKColors.DarkGray);
colors.Add(SKColors.Cornsilk);
colors.Add(SKColors.DarkGreen);
colors.Add(SKColors.DarkMagenta);
colors.Add(SKColors.DarkOliveGreen);
colors.Add(SKColors.DarkOrange);
colors.Add(SKColors.DarkOrchid);
colors.Add(SKColors.DarkRed);
colors.Add(SKColors.DarkSalmon);
colors.Add(SKColors.DarkKhaki);
colors.Add(SKColors.DarkSeaGreen);
colors.Add(SKColors.CornflowerBlue);
colors.Add(SKColors.Chocolate);
colors.Add(SKColors.AntiqueWhite);
colors.Add(SKColors.Aqua);
colors.Add(SKColors.Aquamarine);
colors.Add(SKColors.Azure);
colors.Add(SKColors.Beige);
colors.Add(SKColors.Bisque);
colors.Add(SKColors.Coral);
colors.Add(SKColors.Black);
colors.Add(SKColors.Blue);
colors.Add(SKColors.BlueViolet);
colors.Add(SKColors.Brown);
colors.Add(SKColors.BurlyWood);
colors.Add(SKColors.CadetBlue);
colors.Add(SKColors.Chartreuse);
colors.Add(SKColors.BlanchedAlmond);
colors.Add(SKColors.Transparent);
colors.Add(SKColors.DarkSlateBlue);
colors.Add(SKColors.DarkTurquoise);
colors.Add(SKColors.IndianRed);
colors.Add(SKColors.Indigo);
colors.Add(SKColors.Ivory);
colors.Add(SKColors.Khaki);
colors.Add(SKColors.Lavender);
colors.Add(SKColors.LavenderBlush);
colors.Add(SKColors.HotPink);
colors.Add(SKColors.LawnGreen);
colors.Add(SKColors.LightBlue);
colors.Add(SKColors.LightCoral);
colors.Add(SKColors.LightCyan);
colors.Add(SKColors.LightGoldenrodYellow);
colors.Add(SKColors.LightGray);
colors.Add(SKColors.LightGreen);
colors.Add(SKColors.LemonChiffon);
colors.Add(SKColors.DarkSlateGray);
colors.Add(SKColors.Honeydew);
colors.Add(SKColors.Green);
colors.Add(SKColors.DarkViolet);
colors.Add(SKColors.DeepPink);
colors.Add(SKColors.DeepSkyBlue);
colors.Add(SKColors.DimGray);
colors.Add(SKColors.DodgerBlue);
colors.Add(SKColors.Firebrick);
colors.Add(SKColors.GreenYellow);
colors.Add(SKColors.FloralWhite);
colors.Add(SKColors.Fuchsia);
colors.Add(SKColors.Gainsboro);
colors.Add(SKColors.GhostWhite);
colors.Add(SKColors.Gold);
colors.Add(SKColors.Goldenrod);
colors.Add(SKColors.Gray);
colors.Add(SKColors.ForestGreen);
}
/// <summary>
/// 创建画笔
/// </summary>
/// <param name="color"></param>
/// <param name="fontSize"></param>
/// <returns></returns>
private SKPaint CreatePaint(SKColor color, float fontSize)
{
SkiaSharp.SKTypeface font = SKTypeface.FromFamilyName(null, SKFontStyleWeight.SemiBold, SKFontStyleWidth.ExtraCondensed, SKFontStyleSlant.Upright);
SKPaint paint = new SKPaint();
paint.IsAntialias = true;
paint.Color = color;
paint.Typeface = font;
paint.TextSize = fontSize;
return paint;
}
/// <summary>
/// 获取验证码图片
/// </summary>
/// <param name="lineNum">干扰线数量</param>
/// <param name="lineStrookeWidth">干扰线宽度</param>
/// <returns></returns>
public byte[] GetVerifyCodeImage(int lineNum = 1, int lineStrookeWidth = 1)
{
// 创建bitmap位图
using (SKBitmap image2d = new SKBitmap(SetWith, SetHeight, SKColorType.Bgra8888, SKAlphaType.Premul))
{
// 创建画笔
using (SKCanvas canvas = new SKCanvas(image2d))
{
// 填充背景颜色为白色
if (SetIsRandomColor)
{
SetFontColor = GetRandomColor();
}
// 填充白色背景
canvas.Clear(SetBackgroundColor);
AddForeNoisePoint(image2d);
AddBackgroundNoisePoint(image2d, canvas);
// 将文字写到画布上
var drawStyle = new SKPaint();
drawStyle.IsAntialias = true;
drawStyle.TextSize = SetFontSize;
char[] chars = SetVerifyCodeText.ToCharArray();
for (int i = 0; i < chars.Length; i++)
{
var font = SKTypeface.FromFamilyName(SetFontFamily, SKFontStyleWeight.SemiBold, SKFontStyleWidth.ExtraCondensed, SKFontStyleSlant.Upright);
// 转动的度数
float angle = objRandom.Next(-30, 30);
canvas.Translate(12, 12);
float px = ((i) * SetFontSize);
float py = (SetHeight) / 2;
canvas.RotateDegrees(angle, px, py);
drawStyle.Typeface = font;
drawStyle.Color = SetFontColor;
// 写字 (i + 1) * 16, 28
canvas.DrawText(chars[i].ToString(), px, py, drawStyle);
canvas.RotateDegrees(-angle, px, py);
canvas.Translate(-12, -12);
}
// 画随机干扰线
using (SKPaint disturbStyle = new SKPaint())
{
Random random = new Random();
for (int i = 0; i < lineNum; i++)
{
disturbStyle.Color = colors[random.Next(colors.Count)];
disturbStyle.StrokeWidth = lineStrookeWidth;
canvas.DrawLine(random.Next(0, SetWith), random.Next(0, SetHeight), random.Next(0, SetWith), random.Next(0, SetHeight), disturbStyle);
}
}
// 返回图片byte
using (SKImage img = SKImage.FromBitmap(image2d))
{
using (SKData p = img.Encode(SKEncodedImageFormat.Png, 100))
{
return p.ToArray();
}
}
}
}
}
/// <summary>
/// AddForeNoisePoint
/// </summary>
/// <param name="objBitmap"></param>
private void AddForeNoisePoint(SKBitmap objBitmap)
{
for (int i = 0; i < objBitmap.Width * SetForeNoisePointCount; i++)
{
objBitmap.SetPixel(objRandom.Next(objBitmap.Width), objRandom.Next(objBitmap.Height), SetFontColor);
}
}
/// <summary>
/// AddBackgroundNoisePoint
/// </summary>
/// <param name="objBitmap"></param>
/// <param name="objGraphics"></param>
private void AddBackgroundNoisePoint(SKBitmap objBitmap, SKCanvas objGraphics)
{
using (SKPaint objPen = CreatePaint(SKColors.Azure, 0))
{
for (int i = 0; i < objBitmap.Width * 2; i++)
{
objGraphics.DrawRect(objRandom.Next(objBitmap.Width), objRandom.Next(objBitmap.Height), 1, 1, objPen);
}
}
if (SetIsBackgroundLine)
{
//画图片的背景噪音线
for (var i = 0; i < 12; i++)
{
var x1 = objRandom.Next(objBitmap.Width);
var x2 = objRandom.Next(objBitmap.Width);
var y1 = objRandom.Next(objBitmap.Height);
var y2 = objRandom.Next(objBitmap.Height);
objGraphics.DrawLine(x1, y1, x2, y2, CreatePaint(SKColors.Silver, 0));
}
}
}
#endregion
}
}
根据GitHub文档装了这三个包但也还是在报错
<PackageReference Include="SkiaSharp" Version="2.88.6" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="2.88.6" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.88.6" />
报错信息如下
The type initializer for 'SkiaSharp.SKFontManager' threw an exception.
最后在GitHub上找到了一个解决方案
再添加以下两个包
<PackageReference Include="Microsoft.Maui.Graphics" Version="8.0.3" />
<PackageReference Include="Microsoft.Maui.Graphics.Skia" Version="8.0.3" />
另外Linux还是需要安装libgdiplus组件
apt-get update
apt-get install vim
apt-get install -y libgdiplus && apt-get clean && ln -s /usr/lib/libgdiplus.so /usr/lib/gdiplus.dll
如果是Dockerfile的话则加最后
FROM base AS final
WORKDIR /app
RUN apt-get update -y && apt-get install -y libgdiplus && apt-get clean && ln -s /usr/lib/libgdiplus.so /usr/lib/gdiplus.dll
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "project.dll"]
最后大功告成并附上调用代码
/// <summary>
/// 获取验证码图片
/// </summary>
/// <param name="fileName">默认为空或者传值例如1.png</param>
/// <returns></returns>
[HttpGet]
public IActionResult GetVerifyCodeImage(string? fileName)
{
ValidateCodeHelper helper = new ValidateCodeHelper();
var verifyCodeImage = helper.GetVerifyCodeImage(1, 1);
return File(new MemoryStream(verifyCodeImage), "image/png", fileName);
}