• 【干货】”首个“ .NET Core 验证码组件


    前言

      众所周知,Dotnet Core目前没有图形API,以前的System.Drawing程序集并没有包含在Dotnet Core 1.0环境中。不过在dotnet core labs项目里可以见到MS已经在移植这个项目,不过目前的版本只能在Windows上和NET541+或DNX环境中才可以使用。

      不过在dotnetConf的第两天有一个叫做SkiaSharp的开源项目被提及;它是Google开源的跨平台2D图形API,Skia的.NET封装;目前只能在Full Framework上运行,不过它以后会支持Core。

     现状

      据我了解,Dotnet Core目前没有可用的验证码组件可用,原因就是没有Core的图形接口。所以我的方案是通过开源的图形库来对dotnet core进行支持。

    使用CImg开源库

      CImg 库是一个免费、开源的图像处理C++库,名称原意是 Cool Image,正如其名,CImg是一个非常优秀、功能强大、代码简洁、使用方便的C++ 图像处理库。它不仅非常适合科学家、研究生做科研时使用,也适合在工业应用工程开发中使用,更适合的是,对于有志于开发简洁、高效、功能强大的图像处理库的人而言,CImg的源码是不可多得的学习和参考资料。

      CImg 官网:http://cimg.sourceforge.net/

      可移植性:它完全兼容于操作系统如Windows, Unix, Linux, MacOS X, *BSD...,也完全兼容与编译器如 VC++, g++, icc...等,具有高度的可移植性。

      轻便性:CImg 非常轻便,整个库只用一个文件:cimg.h。任何C++应用程序只需要将该头文件包含进工程中即可使用该库的全部功能。它只定义了四了类(模板)和两个名称空间。该库只依赖与标准C++和STL,只在显示类部分依赖与操作系统的GDI,再也不依赖任何其他的外部库。

    C++封装:

      我把绘图逻辑都放到了一个C++项目中,再用Core项目使用DllImport进行调用。

      而且想到跨平台在Win下我们使用Win32的DLL库进行编译,在Linux下使用g++直接对源代码进行链接编译; 

      下面是项目中最主要的CaptchaImage.cpp,Win32下它会被放到项目中

     1 #include "stdafx.h"
     2 #include "CaptchaImage.h"
     3 #include "CImg.h"
     4 using namespace cimg_library;
     5 
     6 Export_API void GCaptcha(char* file_o, char *captcha_text, int count, int width ,int height , int offset ,int quality, int isjpeg, int fontSize)
     7 {
     8     // Create captcha image
     9     //----------------------
    10     // Write colored and distorted text
    11     CImg<unsigned char> captcha(width, height, 1, 3, 0), color(3);
    12     const unsigned char red[] = { 255,0,0 }, green[] = { 0,255,0 }, blue[] = { 0,0,255 };
    13 
    14     char letter[2] = { 0 };
    15     for (unsigned int k = 0; k<count; ++k) {
    16         CImg<unsigned char> tmp;
    17         *letter = captcha_text[k];
    18         if (*letter) {
    19             cimg_forX(color, i) color[i] = (unsigned char)(128 + (std::rand() % 127));
    20             tmp.draw_text((int)(2 + 8 * cimg::rand()),
    21                           (int)(12 * cimg::rand()), letter, red, 0, 1, fontSize).resize(-100, -100, 1, 3);
    22 
    23             const float sin_offset = (float)cimg::crand() * 3, sin_freq = (float)cimg::crand() / 7;
    24             
    25             cimg_forYC(captcha, y, v) captcha.get_shared_row(y, 0, v).shift((int)(4 * std::cos(y*sin_freq + sin_offset)));
    26             
    27             captcha.draw_image(count + offset * k, tmp);
    28         }
    29     }
    30 
    31     // Add geometric and random noise
    32     CImg<unsigned char> copy = (+captcha).fill(0);
    33     for (unsigned int l = 0; l<3; ++l) {
    34         if (l) copy.blur(0.5f).normalize(0, 148);
    35         for (unsigned int k = 0; k<10; ++k) {
    36             cimg_forX(color, i) color[i] = (unsigned char)(128 + cimg::rand() * 127);
    37             if (cimg::rand() < 0.5f) {
    38                 copy.draw_circle((int)(cimg::rand()*captcha.width()),
    39                                      (int)(cimg::rand()*captcha.height()),
    40                                      (int)(cimg::rand() * 30),
    41                                      color.data(), 0.6f, ~0U);
    42             }
    43             else {
    44                 copy.draw_line((int)(cimg::rand()*captcha.width()),
    45                                     (int)(cimg::rand()*captcha.height()),
    46                                     (int)(cimg::rand()*captcha.width()),
    47                                     (int)(cimg::rand()*captcha.height()),
    48                                     color.data(), 0.6f);
    49             }
    50         }
    51     }
    52     captcha |= copy;
    53     captcha.noise(10, 2);
    54 
    55     captcha = (+captcha).fill(255) - captcha;
    56 
    57     // Write output image and captcha text
    58     //-------------------------------------
    59     //std::printf("%s
    ",captcha_text);
    60 
    61     if (isjpeg) {
    62         captcha.save_jpeg(file_o, quality);
    63     }
    64     else {
    65         captcha.save(file_o);
    66     }
    67 
    68 }
    CaptchaImage.cpp

      头文件:

    1 #ifdef PROJECT_EXPORTS
    2 #define Export_API extern "C" __declspec(dllexport)
    3 #else
    4 #define Export_API extern "C"
    5 #endif
    6 
    7 Export_API void GCaptcha(char* file_o, char *captcha_text, int count, int width, int height, int offset, int quality, int isjpeg, int fontSize);
    CaptchaImage.h

      这里为了跨平台编译我将stdafx.h文件进行了修改如下:

    1 #pragma once
    2 #ifdef _MSC_VER //I'm in VS
    3 #include "targetver.h"
    4 #define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
    5 // Windows Header Files:
    6 #include <windows.h>
    7 #endif
    stdafx.h

    Dotnet Core平台封装:

      1  public class CaptchaImageCore
      2     {
      3 
      4         [DllImport("libcaptchaimage.so", EntryPoint = "GCaptcha")]
      5         public static extern void libCaptcha(string file_o, string captcha_text, int count, int width, int height, int offset, int quality, int isjpeg, int fontSize);
      6 
      7 
      8         [DllImport("libcaptchaimage.dll", EntryPoint = "GCaptcha")]
      9         public static extern void GCaptcha(string file_o, string captcha_text, int count, int width, int height, int offset, int quality, int isjpeg, int fontSize);
     10 
     11         public string Text { set; get; }
     12 
     13         public int ImageWidth { set; get; }
     14         public int ImageHeight { set; get; }
     15 
     16         public int ImageOffset { set; get; }
     17 
     18         public int ImageQuality { set; get; }
     19 
     20         public int FontSize { set; get; }
     21 
     22 
     23         public CaptchaImageCore(int w,int h,int fontSize)
     24         {
     25             this.ImageWidth = w;
     26             this.ImageHeight = h;
     27             this.FontSize = fontSize;
     28             this.ImageOffset = 40;
     29             this.ImageQuality = 100;
     30             
     31         }
     32 
     33         public MemoryStream GetStream(string fileName)
     34         {
     35             this.Save(fileName);
     36             MemoryStream ms = new MemoryStream();
     37             using (var fileStream = new FileStream(fileName, FileMode.Open))
     38             {
     39                 fileStream.CopyTo(ms);
     40             }
     41             try
     42             {
     43                 File.Delete(fileName);
     44             }
     45             catch { }
     46             return ms;
     47 
     48         }
     49 
     50 
     51         public void Save(string fileName)
     52         {
     53             if (string.IsNullOrEmpty(fileName))
     54                 throw new NullReferenceException("file name is null");
     55             if (string.IsNullOrEmpty(this.Text))
     56                 this.Text = this.GenerateCheckCode();
     57 
     58 
     59 
     60             if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
     61             {
     62                 GCaptcha(fileName, this.Text, this.Text.Length, this.ImageWidth, this.ImageHeight,
     63                                           this.ImageOffset, this.ImageQuality, 0, this.FontSize);
     64             }
     65             else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
     66             {
     67                 libCaptcha(fileName, this.Text, this.Text.Length, this.ImageWidth, this.ImageHeight,
     68                                          this.ImageOffset, this.ImageQuality, 0, this.FontSize);
     69             }
     70         }
     71 
     72         public string GenerateCheckCode()
     73         {
     74             int number;
     75             char code;
     76             string checkCode = String.Empty;
     77 
     78             System.Random random = new Random();
     79 
     80             for (int i = 0; i < 5; i++)
     81             {
     82                 number = random.Next();
     83 
     84                 if (number % 2 == 0)
     85                     //生成'0'-'9'字符
     86                     code = (char)('0' + (char)(number % 10));
     87                 else
     88                     //生成'A'-'Z'字符
     89                     code = (char)('A' + (char)(number % 26));
     90 
     91                 checkCode += code.ToString();
     92             }
     93 
     94             return checkCode;
     95             //两个字符相加等于=asicc码加
     96         }
     97 
     98 
     99 
    100     }
    LibCaptchaImageWarp.cs

     编译:

    Win32编译就不用说了,直接在VS2015里编译就好,但是一定要注意的是,要编译为X64平台的目标代码,因为我们的Dotnet Core只支持x64平台;

    主要说下Linux编译,目前我只在Ubuntu 14.04进行了编译测试,编译时CImg依赖也X11,所以要在编译环境中安装X11开发库,当然Ubuntu也需要64位;

    sudo apt-get install libx11-dev

    接下下是编译:

    把那个Win32项目Copy到Linux中,然后Bash到目录下执行:

    g++ CaptchaImage.cpp -fPIC -shared -o libcaptchaimage.so

    然后当你发布程序时一定要将libcaptachaImageWarp.dll 和 Win32 Dll 或 libcaptchaimage.so 文件一起放到程序执行目录。

    最后:

    看看效果吧:

    验证码源码:https://github.com/maxzhang1985/YOYOFx/tree/master/Native

    Demo:https://github.com/maxzhang1985/YOYOFx/tree/master/CoreHost

    QQ群:214741894

    Demo和源码在:https://github.com/maxzhang1985/YOYOFx

    YOYOFx是一个基于Core和Owin的框架,项目没有依赖微软的MVC框架,支持在.net 4.5和Mono上直接SelfHost或使用Tinyfox跨平台运行, 也支持在Dotnet Core 1.0 RC2 实现跨平台运行; 框架刚刚写出来还没有文档,请大家见谅。

    欢迎大家Star和Fork

     

  • 相关阅读:
    最近有人说我欺骗消费者,今天来一波视频分享
    前端 Java Python等资源合集大放送
    dubbo源码学习(四):暴露服务的过程
    dubbo源码学习(二) : spring 自定义标签
    Dubbo多注册中心和Zookeeper服务的迁移
    线程各种状态转换分析
    java并发之同步辅助类CountDownLatch
    工作5年的Java程序员,才学会阅读源码,可悲吗?
    【阿里面试系列】Java线程的应用及挑战
    「阿里面试系列」搞懂并发编程,轻松应对80%的面试场景
  • 原文地址:https://www.cnblogs.com/maxzhang1985/p/5584528.html
Copyright © 2020-2023  润新知