DotNet – 清风亦平凡 https://www.mlplus.net 关注IT世界,记录平凡生活 Sun, 30 Jul 2023 12:22:46 +0000 zh-CN hourly 1 https://wordpress.org/?v=6.4.3 Syncfusion.DocIO.Net.Core操作Word的第三方组件无限制版 https://www.mlplus.net/2023/05/16/syncfusion-docio-net-core/ https://www.mlplus.net/2023/05/16/syncfusion-docio-net-core/#respond Tue, 16 May 2023 04:54:47 +0000 https://www.mlplus.net/?p=4208 背景

一些朋友推荐处理Word可以使用Syncfusion.DocIO,说是API比较好用而且还支持doc格式的文件,因此就了解一下。经过测试确认是比一些开源的处理库好用一点,支持相对来说比较完善,就因为这些所以才会有这篇介绍的内容。

Syncfusion.DocIO.Net.Core

Syncfusion.DocIO.Net.CoreSyncfusion的一组.NET组件,用于创建、编辑和转换Microsoft Word文档。它支持多种文件格式,包括.docx.rtf.txt等。它提供了一组易于使用的 API,可以帮助开发人员轻松地创建、读取和修改 Word 文档。

主要功能

  • 创建Word文档并添加内容、表格、图片等元素。
  • 编辑Word文档中的文本、表格、图片等内容。
  • Word文档转换为其他格式,如PDFHTML等。
  • 支持批量操作,可以同时打开多个Word文档并进行批量编辑和转换。
  • 支持自定义样式和主题,可以根据需要修改文档的外观和布局。
  • 包含额外的安全性特性,可以确保在处理敏感数据时的安全性

使用示例

以下是一个简单的示例,演示如何使用Syncfusion.DocIO.Net.Core创建一个Word文档并添加内容


using Syncfusion.DocIO;
using Syncfusion.DocIO.Xls;
using Syncfusion.DocIO.Xls.Conversion;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CreateWordDocument
{
    class Program
    {
        static void Main(string[] args)
        {
            //创建一个新的Word文档
            Document doc = new Document();
            doc.AddSection("Introduction");
            
            //添加一个标题1级别的段落,并在其中添加一些文本
            Section section = doc.Sections[0];
            paragraph = section.Paragraphs.Add("Hello World!", DocFormat.Title1);
            paragraph.Range.InsertAfter("This is a sample document created using Syncfusion.DocIO.NET Core.");
            
            //保存并关闭文档
            doc.Save(@"C:\Documents\Sample.docx");
            doc.Close();
        }
    }
}



结论

Syncfusion.DocIO.Net.Core相对来说API比较好用,操作的格式基本符合预期。缺点就是此组件是商业版本,会有水印以及其他版权提示内容,如下图所示。如果使用比较低的版本,只有页眉页脚有试用版说明,比如:20.1.0.61及一下版本。

Word使用Syncfusion.DocIO导出包含水印与其他说明。

鉴于水印不便于学习,为了更好的营造学习气氛,所以对水印以及其他提示进行移除处理,水印移除后的表现形式如下:

Syncfusion.DocIO.Net.Core操作Word的第三方组件无限制版-第1张图片

如果不是非Syncfusion.DocIO.Net.Core不可,可以选用其他免费开源的Word处理组件。如果一定要使用请购买正版,以便得到更好的效果以及技术支持。虽然此组件相对来说比较完善好用,但最终我还是选用其他的开源组件,例如:

下载

Syncfusion.DocIO.Net.Core21.2.4 Syncfusion.DocIO.Net.Core22.1.37
版本22.1.37中包含Doc、XlsIO、Pdf
以上下载内容仅供学习使用,请勿用于其他用途。



转载请注明:清风亦平凡 » Syncfusion.DocIO.Net.Core操作Word的第三方组件无限制版

]]>
https://www.mlplus.net/2023/05/16/syncfusion-docio-net-core/feed/ 0
使用ABP框架UI找不到文件的异常 https://www.mlplus.net/2022/10/05/abp-install-libs/ https://www.mlplus.net/2022/10/05/abp-install-libs/#respond Wed, 05 Oct 2022 14:03:03 +0000 https://www.mlplus.net/?p=4000 背景

自己的一个小应用使用了ABP框架最新内容创建了一个项目模板,为了快速的完成第一版就是用来ABP自带的UI框架。在测试项目模板正常与否的时候发现关于UI方面的异常信息,第一次使用ABP官方提供的UI框架,所以先在此做一下记录。

以下是异常信息:

AbpException: Could not find the bundle file ‘/libs/abp/core/abp.css’ for the bundle ‘LeptonXLite.Global’!

ABP框架UI异常图片

解决方法

安装ABP框架UI依赖包abp install-libs



转载请注明:清风亦平凡 » 使用ABP框架UI找不到文件的异常

]]>
https://www.mlplus.net/2022/10/05/abp-install-libs/feed/ 0
.Net Core获取Window系统机器码 https://www.mlplus.net/2022/10/04/dot-net-core-machinecode/ https://www.mlplus.net/2022/10/04/dot-net-core-machinecode/#respond Tue, 04 Oct 2022 06:27:45 +0000 https://www.mlplus.net/?p=3989 背景

因为朋友想做一个软件认证相关的东西,所以考虑到关于电脑唯一标识机器码的问题。关于机器码之前并没有真正实现过,也只是了解大概的原理。今天就做一下简单记录,以便以后使用。

机器码

机器码指的是软件根据计算机的硬件信息,例如:CPU、内存、主板序列号等,按照一定的算法生成的一串无规律的字符串,并且在不同计算机上生成的机器码是不一样的,因为每台计算机的硬件信息不一样。机器码,也叫序列号认证码注册申请码等。

代码实现


using System.Management;
using System.Runtime.InteropServices;

namespace HonourWorld.Core.Common
{
    public sealed class MachineCode
    {
        private readonly static MachineCode machineCode=new MachineCode();
        public static string GetMachineCodeString()
        {
            string machineCodeString = $"PC.{machineCode.GetCpuInfo()}.{machineCode.GetHDid()}.{machineCode.GetDiskSerialNumber()}.{machineCode.GetMacAddress()}.{machineCode.GetBiosSerialNumber()}";
            return machineCodeString;
        }
        private MachineCode()
        { 
        }
        ///   <summary>
        ///   获取cpu序列号
        ///   </summary>
        ///   <returns> string </returns>
        public string GetCpuInfo()
        {
            string? cpuInfo = "";
            try
            {
                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    using (ManagementClass cimobject = new ManagementClass("Win32_Processor"))
                    {
                        ManagementObjectCollection moc = cimobject.GetInstances();
                        foreach (ManagementObject mo in moc)
                        {
                            if (mo == null || mo.Properties == null)
                            {
                                continue;
                            }
                            var info = mo.Properties["ProcessorId"];
                            if (info!=null)
                            {
                                cpuInfo = info.Value.ToString();
                            }
                            mo?.Dispose();
                        }
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
            return (cpuInfo??"").Trim();
        }
        ///   <summary>
        ///   获取硬盘ID
        ///   </summary>
        ///   <returns> string </returns>
        public string GetHDid()
        {
            string HDid = "";
            try
            {
                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    using (ManagementClass cimobject1 = new ManagementClass("Win32_DiskDrive"))
                    {
                        ManagementObjectCollection moc1 = cimobject1.GetInstances();
                        foreach (ManagementObject mo in moc1)
                        {
                            HDid = (string)mo.Properties["Model"].Value;
                            mo.Dispose();
                        }
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
            return HDid.Trim();
        }

        /// <summary>
        /// 获得硬盘序列号
        /// </summary>
        /// <returns></returns>
        public string GetDiskSerialNumber()
        {
            string serialNumber = "";
            try
            {
                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    using (ManagementClass bios = new ManagementClass("Win32_PhysicalMedia"))
                    {
                        ManagementObjectCollection moc1 = bios.GetInstances();
                        foreach (ManagementObject mo in moc1)
                        {
                            serialNumber = (string)mo.Properties["SerialNumber"].Value;
                            mo.Dispose();
                        }
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
            return serialNumber.Trim();
        }

        ///   <summary>
        ///   获取网卡硬件地址
        ///   </summary>
        ///   <returns> string </returns>
        public string GetMacAddress()
        {
            string mocAddress = "";
            try
            {
                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    using (ManagementClass mc = new ManagementClass("Win32_NetworkAdapterConfiguration"))
                    {
                        ManagementObjectCollection moc2 = mc.GetInstances();
                        foreach (ManagementObject mo in moc2)
                        {
                            if ((bool)mo["IPEnabled"]&& mo["MacAddress"]!=null)
                            {
                                mocAddress = (string)mo["MacAddress"];
                            }
                            mo.Dispose();
                        }
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
            return mocAddress.Trim();
        }

        /// <summary>
        /// 获得主板序列号
        /// </summary>
        /// <returns></returns>
        public string GetBiosSerialNumber()
        {

            string serialNumber = "";
            try
            {
                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    using (ManagementClass bios = new ManagementClass("Win32_BaseBoard"))
                    {
                        ManagementObjectCollection moc1 = bios.GetInstances();
                        foreach (ManagementObject mo in moc1)
                        {
                            serialNumber = (string)mo.Properties["SerialNumber"].Value;
                            mo.Dispose();
                        }
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
            return serialNumber.Trim();
        }

    }
}

调用方式


        private void btnIntroduction_Click(object sender, EventArgs e)
        {
            string machineCode = Honour.MachineCode.GetMachineCodeString();
            machineCode = EncryptionSha.GetSha1HashString(machineCode);
            machineCode = Regex.Replace(machineCode, @"\w{8}", "$0-", RegexOptions.IgnoreCase).TrimEnd('-');
            var result = MessageBox.Show($"机器码:{machineCode}", "提示", MessageBoxButtons.OK);
        }

机器码是使用CPU序列号硬盘ID硬盘序列号MAC地址主板序列号进行拼接的字符串。由于电脑硬件信息拼接而成的无规则字符串,所以显得杂乱。为了让其看上去比较规范就对拼接的取散列信息,散列算法有很多种,例如:MD5SHA1等。这里使用SHA1来实现需求。

机器码

示例下载

.Net Core机器码示例
以上获取机器相关信息的方式方法只支持Windows系统。



转载请注明:清风亦平凡 » .Net Core获取Window系统机器码

]]>
https://www.mlplus.net/2022/10/04/dot-net-core-machinecode/feed/ 0
判断登录的QQ是否已经加入指定的QQ群之二 https://www.mlplus.net/2022/09/25/check-qq-in-group/ https://www.mlplus.net/2022/09/25/check-qq-in-group/#comments Sun, 25 Sep 2022 10:32:10 +0000 https://www.mlplus.net/?p=3966 背景

任何事情都有一个原由,本篇内容也不例外。如标题所示,关于判断登录的QQ是否已经加入指定的QQ群的问题。为什么是之二?因为之前已经写过一次相关内容了。既然曾经已经写过一次为什么还要写第二次?因为今天早上收到了一个邮件通知,这个是博客评论通知,有人评论就以邮件形式告知。评论内容是这样的:c#那个QQ群验证已经无法使用了能更新下吗。如下图所示:

关于C#QQ群验证的评论回复图

没有错,就如我回复的一样,示例程序的代码的确不可以用了,不过思路依然可以使用。

分析

其实没有什么好分析的,思路与上次一样。只是上次验证的地址(http://qun.qzone.qq.com)无效了,可能是下线了吧!不过QQ也有关于群里管理的网站(https://qun.qq.com/),这个网站依然可以实现这样的操作。实现比较简单,访问网址https://qun.qq.com/member.html并登录,然后通过正则表达式匹配就行了!

QQ群管理

此次还是与上次有些变化的,网页登录竟然取消了账号与密码登录,只能手机扫码或者选择已经登录的账号。也许是为了安全考虑吧,不过这也不重要了!

实现

这里使用Microsoft .netWindows Form实现示例,使用WebBrowser(其实也可以使用CefSharpWebView2等控件)控件加载网址https://qun.qq.com/member.html登录成功后,有使用正则匹配指定的群号来完成操作。

代码大致如下:


using System;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace QQGroupVerification
{
    public partial class QQVerificationLogin : Form
    {
        public QQVerificationLogin()
        {
            InitializeComponent();
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            webBrowser1.Navigate("https://xui.ptlogin2.qq.com/cgi-bin/xlogin?pt_disable_pwd=1&appid=715030901&daid=73&hide_close_icon=1&pt_no_auth=1&s_url=https%3A%2F%2Fqun.qq.com%2Fmember.html%23");
            webBrowser1.ScriptErrorsSuppressed=true;
            webBrowser1.DocumentCompleted += WebBrowser1_DocumentCompleted;
        }

        private void WebBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            if (webBrowser1.ReadyState == WebBrowserReadyState.Complete&&webBrowser1.Url.ToString().StartsWith("https://qun.qq.com"))
            {
                webBrowser1.Hide();
                string webPageString = webBrowser1.Document.Body.InnerHtml;
                webPageString = Regex.Replace(webPageString, @"<script\s?type=""text/template""\s+?id=.+?>[\s\S]+?</script>", "", RegexOptions.IgnoreCase);
                var match = Regex.Match(webPageString, @"<div\sclass=.?body-content.?>[\s\S]+?<div\sclass=.?member-body.?>[\s\S]+(?=<div\s+?class=""foot\sbtn-footer"")", RegexOptions.IgnoreCase);
                if (match == null|| !match.Success)
                {
                    return;
                }
                string groupid =this.textBox1.Text;// "这里是你的QQ群ID";
                if (Regex.IsMatch(match.Value, $"data-id=\"{groupid}\""))
                {
                    MessageBox.Show("验证成功");
                    CloseChrome();
                    ProgramMain form2 = new ProgramMain();
                    form2.Show();

                }
                else
                {
                    MessageBox.Show("请加指定交流群入本群");
                    CloseChrome();
                    Application.Exit();
                }
            }
            bool isBusy = webBrowser1.IsBusy;
        }
      
        public void CloseChrome()
        {
            if (webBrowser1 != null)
            {
                webBrowser1.Dispose();
            }
        }

    }
}

此次代码与上次相比主要是多了两个正则,为什么要多这两个正则?新的QQ群管理网站用了前后端分离用了一些模板,这些模板可能会干扰接下来的操作。所以,第一个正则表达式替换掉所有JavaScript模板相关代码,替换为空字符串。第二个正则用来获取QQ群里列表区域所有文本内容,接下来才是判断返回信息中是否包含指定的QQ群。

预览

判断登录的QQ是否已经加入指定的QQ群之二-第2张图片
判断登录的QQ是否已经加入指定的QQ群之二-第3张图片

另一种方式

这一种方式是使用Windows Api查找窗体句柄,要打开指定的群,找到了窗体就认为加入了群。不过这个方法是不精准的,这个通过查找窗体的标题。知道要验证QQ群的名称,随便建一个临时群修改名称为要验证QQ群名称就可以过掉验证。

实现


using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace QQGroupVerificationByWindowsApi
{
    public class WindownApiVerification
    {
        [DllImport("user32.dll", EntryPoint = "FindWindow")]
        private static extern IntPtr FindWindow(string IpClassName, string IpWindowName);
        /// <summary>
        /// 找到句柄
        /// </summary>
        /// <param name="IpClassName">类名</param>
        /// <returns></returns>
        public static IntPtr GetHandle(string IpClassName, string IpWindowName)
        {
            return FindWindow(IpClassName, IpWindowName);

        }
        /// <summary>
        /// 判断是否打开了某指定的QQ群
        /// </summary>
        /// <param name="qqGroupName">参数是群名</param>
        /// <returns></returns>
        public static bool IsInQQGroup(string qqGroupName) 
        {
            IntPtr result = GetHandle("TXGuiFoundation", qqGroupName);
            if (result == IntPtr.Zero)
            {
                return false;
            }
            return true;
        }
    }
}

使用方式如下:


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace QQGroupVerificationByWindowsApi
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            string qqGroup = textBox1.Text;
            if(string.IsNullOrWhiteSpace(qqGroup))
            {

                MessageBox.Show("请输入需要验证的QQ群的名称","提示",MessageBoxButtons.OK,MessageBoxIcon.Information);
                return;
            }
           var result=WindownApiVerification.IsInQQGroup(qqGroup);
            if (result)
            {
                MessageBox.Show("存在", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            MessageBox.Show("不存在", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
    }
}

预览

判断登录的QQ是否已经加入指定的QQ群之二-第4张图片

示例下载

判断登录的QQ是否已经加入指定的QQ群

总结

以上就是关于判断QQ是否加入指定QQ群的简单判断,使用哪种方式就看个人想法了!其实还有其他的一些方法,例如:内存、网络等。不过就不再进行深入了解了,实现其他的方式现在也没有意义。无论哪种方式都可以被绕过,有时候不必太过执着。

警告:本章内容仅供学习交流,请勿使用非法用途。有任何非法行为均需自行承担!!!!



转载请注明:清风亦平凡 » 判断登录的QQ是否已经加入指定的QQ群之二

]]>
https://www.mlplus.net/2022/09/25/check-qq-in-group/feed/ 6
.NET CORE实现SHA1 https://www.mlplus.net/2022/09/24/net-core-sha1/ https://www.mlplus.net/2022/09/24/net-core-sha1/#comments Sat, 24 Sep 2022 14:34:21 +0000 https://www.mlplus.net/?p=3956 背景

因业务需求,需要保存一些指定的字符串,但这些字符串可能相同。为了避免重复存储造成数据大量冗余,所以需要对这些字符串进行一些散列计算。使这一些相同的字符串产生唯一的标识,实现业务上的需求。关于字符串散列算法还是比较多的,例如:MD2MD4MD5Sha1Sha256Sha512等等,在结合字符串散列碰撞以及散列最终长度考虑,最后选中Sha1作为此次散列算法。

SHA-1

(英语:Secure Hash Algorithm 1,中文名:安全散列算法1)是一种密码散列函数,美国国家安全局设计,并由美国国家标准技术研究所(NIST)发布为联邦数据处理标准(FIPS)。SHA-1可以生成一个被称为消息摘要的160位(20字节)散列值,散列值通常的呈现形式为40个十六进制数。

.NET CORE 实现 SHA1

以下是使用.Net Core自带类库实现Sha1的简单方法。

    
public static class EncryptionSha
{
        public static byte[] GetSha1Hash(string inputString)
        {
            var sha1 = SHA1.Create();
            byte[] data = sha1.ComputeHash(Encoding.UTF8.GetBytes(inputString));
            sha1.Dispose();
            return data;
        }

        public static string GetSha1HashString(string inputString)
        {
            StringBuilder sb = new StringBuilder();
            byte[] hashByte = GetSha1Hash(inputString);
            foreach (byte b in hashByte)
            {
                sb.Append(b.ToString("X2"));
            }
            return sb.ToString();
        }
    }

使用方式


using System;

namespace TestSha1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(EncryptionSha.GetSha1HashString("Hello World!"));
            Console.ReadKey();
        }
    }
}
.net core使用Sha1散列调用方式

示例

SHA1散列



转载请注明:清风亦平凡 » .NET CORE实现SHA1

]]>
https://www.mlplus.net/2022/09/24/net-core-sha1/feed/ 2
.NET CORE引用Aspose.Words的异常Could not load file or assembly https://www.mlplus.net/2022/08/01/net-core-could-not-load-file-or-assembly/ https://www.mlplus.net/2022/08/01/net-core-could-not-load-file-or-assembly/#respond Mon, 01 Aug 2022 07:22:50 +0000 https://www.mlplus.net/?p=3897 背景

引用了一个第三方的dll库,编译无任何异常提示,但是运行就报异常。自从有了Nuget基本没有再使用直接引用dll这种方式了。经过确认dll也设置了输出到目录,在 bin目录也的确存在此dll库。

异常信息

Could not load file or assembly ‘Aspose.Words, Version=20.6.0.0, Culture=neutral, PublicKeyToken=716fcc553a201e56’. 系统找不到指定的文件。
System.IO.FileNotFoundException: Could not load file or assembly ‘Aspose.Words, Version=20.6.0.0, Culture=neutral, PublicKeyToken=716fcc553a201e56’. 系统找不到指定的文件。
File name: ‘Aspose.Words, Version=20.6.0.0, Culture=neutral, PublicKeyToken=716fcc553a201e56’

.NET CORE引用Aspose.Words的异常Could not load file or assembly-第0张图片

解决

查看下引用dll库的项目文件,项目文件中少了dll的相关版本号。


<Reference Include="Aspose.Words">

在这条记录中添加对应的版本号后,问题得到解决!如下:


<Reference Include="Aspose.Words" Version="20.6.0.0">



转载请注明:清风亦平凡 » .NET CORE引用Aspose.Words的异常Could not load file or assembly

]]>
https://www.mlplus.net/2022/08/01/net-core-could-not-load-file-or-assembly/feed/ 0
.net core在Linux系统报Gdip异常的问题 https://www.mlplus.net/2022/04/08/net-core-linux-system-gdip-error/ https://www.mlplus.net/2022/04/08/net-core-linux-system-gdip-error/#respond Fri, 08 Apr 2022 01:00:00 +0000 https://www.mlplus.net/?p=3844 背景

.net core 应用中使用了Excel文件导出,发现Excel导出失败,导出的操作出现了异常。看了一下是Gdip的这个异常。这个异曾经见到过,记得也很快解决了,不晓得为什么又会出现呢?仔细看了一下,详细错误还是有点差别。经过确认上一次安装的libc6-devlibgdiplus是存在的,并没有丢失或者损坏。

根据异常信息,基本可以确定是引用System.Drawing.Common类库引起的问题,经过排查发现引用的Excel组件Magicodes.IE.Excel.Abp包中有使用System.Drawing.Common。前几天还好好的,现在为何System.Drawing.Common不支持非Windows平台呢?只是确定引用的System.Drawing.Common库的版本是6.0,其他的就没有太多线索。

最后又看一遍异常信息,根据异常提示打开所提示的网址看一下:

https://aka.ms/systemdrawingnonwindows

提示中断性变更,仅在Windows上支持 System.Drawing.Common

System.Drawing.Common NuGet 包现在被归为 Windows 特定的库。 在为非 Windows 操作系统编译时,平台分析器会在编译时发出警告。

在非 Windows 操作系统上,除非设置了运行时配置开关,否则将引发 TypeInitializationException 异常,其中 PlatformNotSupportedException 作为内部异常。

旧行为

.NET 6 之前,使用 System.Drawing.Common 包不会产生任何编译时警告,也不会引发任何运行时异常。

新行为

.NET 6 开始,当为非 Windows 操作系统编译引用代码时,平台分析器会发出编译时警告。 此外,除非设置了配置选项,否则将引发以下运行时异常:

.net core在Linux系统报Gdip异常的问题-第0张图片

The type initializer for ‘Gdip’ threw an exception.
System.TypeInitializationException: The type initializer for ‘Gdip’ threw an exception.
—> System.PlatformNotSupportedException: System.Drawing.Common is not supported on non-Windows platforms. See https://aka.ms/systemdrawingnonwindows for more information.
at System.Drawing.LibraryResolver.EnsureRegistered()
at System.Drawing.SafeNativeMethods.Gdip.PlatformInitialize()
at System.Drawing.SafeNativeMethods.Gdip..cctor()
— End of inner exception stack trace —
at System.Drawing.SafeNativeMethods.Gdip.GdipGetGenericFontFamilySansSerif(IntPtr& fontfamily)
at System.Drawing.FontFamily.GetGdipGenericSansSerif()
at System.Drawing.FontFamily.get_GenericSansSerif()
at System.Drawing.Font.CreateFont(String familyName, Single emSize, FontStyle style, GraphicsUnit unit, Byte charSet, Boolean isVertical)
at System.Drawing.Font..ctor(String familyName, Single emSize, FontStyle style, GraphicsUnit unit, Byte gdiCharSet, Boolean gdiVerticalFont)
at OfficeOpenXml.ExcelRangeBase.AutoFitColumns(Double MinimumWidth, Double MaximumWidth)
at OfficeOpenXml.ExcelRangeBase.AutoFitColumns(Double MinimumWidth)
at OfficeOpenXml.ExcelRangeBase.AutoFitColumns()
at OfficeOpenXml.ExcelColumn.AutoFit()
at Magicodes.ExporterAndImporter.Excel.Utility.ExportHelper1.AddStyle() at Magicodes.ExporterAndImporter.Excel.Utility.ExportHelper1.AddHeaderAndStyles()
at Magicodes.ExporterAndImporter.Excel.ExcelExporter.ExportHeaderAsByteArray(String[] items, String sheetName)

引入的版本

此更改会影响源兼容性和二进制兼容性。

更改原因

由于 System.Drawing.Common 被设计为 Windows 技术的精简包装器,因此其跨平台实现欠佳。

libgdiplus 是本机端 System.Drawing.Common 跨平台实现的主要提供程序。 libgdiplus 实际上是对 System.Drawing.Common 所依赖的 Windows 部分的重新实现。 该实现使 libgdiplus 成为一个重要的组件。 它大约有 30,000C 代码,大部分未经测试,而且缺少很多功能。 libgdiplus 还具有许多用于图像处理和文本呈现的外部依赖项,例如 cairopango 和其他本机库。 这些依赖项使得维护和交付组件更具挑战性。 自从包含 Mono 跨平台实现以来,我们已将许多从未得到修复的问题重定向到 libgdiplus。 相比之下,我们采用的其他外部依赖项,例如 icu 或 openssl,都是高质量的库。 使 libgdiplus 的功能集和质量与 .NET 堆栈的其余部分相媲美是不可行的。

通过对 NuGet 包的分析,我们观察到 System.Drawing.Common 主要用于跨平台的图像处理,例如 QR 代码生成器和文本呈现。 由于我们的跨平台图形支持不完整,我们还没有注意到大量的图形使用。 System.Drawing.Common 在非 Windows 环境中的使用通常得到 SkiaSharp ImageSharp 的良好支持。

System.Drawing.Common 将仅在 Windows 窗体和 GDI+ 的上下文中继续演变。

建议的操作

或者,可通过将 runtimeconfig.json 文件中的 System.Drawing.EnableUnixSupportSystem.Drawing.EnableUnixSupport设置为 true 来启用对 .NET 6 中非 Windows 平台的支持:

{
   "configProperties": {
      "System.Drawing.EnableUnixSupport": true
   }
}

添加此配置开关是为了让严重依赖此包的跨平台应用有时间迁移到更新式的库。 但是,不会修复非 Windows bug。 此外,此开关已在 .NET 7 中删除。

说白了就是在非Windows平台下.net 6中默认不支持System.Drawing.Common了,想继续使用要设置运行时配置,并且以后.NET 7中这个配置也要干掉。

问题解决

又核实了一遍,确认是引用了.Net 6.0版本下的System.Drawing.Common,应该是其他人引用了6.0版本导致的。目前应用使用的是.net 5,配置是不可能修改配置的。所以,最简单的办法就是降级,将System.Drawing.Common版本降级到5.0,问题就此解决。

.NET 5或更早版本解决

docker环境下使用EPPlus 导出Excel报Gdip异常

基于dotnet官方的aspnet5的镜像构建安装libgdiplus基础镜像



转载请注明:清风亦平凡 » .net core在Linux系统报Gdip异常的问题

]]>
https://www.mlplus.net/2022/04/08/net-core-linux-system-gdip-error/feed/ 0
C#使用正则表达式移除字串符前后指定的字符串 https://www.mlplus.net/2022/04/04/csharp-regex-trim-remove/ https://www.mlplus.net/2022/04/04/csharp-regex-trim-remove/#respond Mon, 04 Apr 2022 15:49:55 +0000 https://www.mlplus.net/?p=3819 有时候一些需求,移除字符串前后指定的字符串。其实,如果没有仅移除一次的需求,使用系统自带的方法即可完成,使用TrimTrimEndTrimStart方法也比较快捷。当有一些特殊需求的时候,这些方法就不再方便。所以,就使用正则表达式简单的实现相关需求。

字符串扩展代码实现


    public static partial class Extensions
    {
        /// <summary>
        /// 移除字符串前后指定的字符串
        /// </summary>
        /// <param name="value">字符串本身</param>
        /// <param name="trimContent">将要移除字符前后的指定内容</param>
        /// <param name="isRepeat">是否允许重复匹配</param>
        /// <returns></returns>
        public static string Trim(this string value, string trimContent = "",bool isRepeat=true)
        {
            if (string.IsNullOrWhiteSpace(value))
            {
                return "";
            }
            if (string.IsNullOrWhiteSpace(trimContent))
            {
                return value.Trim();
            }
            trimContent = Regex.Replace(trimContent, @"([.$\\^*+?\[\]()])", @"\$1");
            string limit = isRepeat ? "+" : "";
            string regexString = $"^(?:{trimContent}){limit}|(?:{trimContent}){limit}$";
            value = Regex.Replace(value, regexString, "");
            return value;
        }

        /// <summary>
        /// 移除字符串开始指定的字符串
        /// </summary>
        /// <param name="value">字符串本身</param>
        /// <param name="trimContent">将要在字符串开始移除的字符</param>
        /// <param name="isRepeat">是否允许重复匹配</param>
        /// <returns></returns>
        public static string TrimStart(this string value, string trimContent = "", bool isRepeat = true)
        {
            if (string.IsNullOrWhiteSpace(value))
            {
                return "";
            }
            if (string.IsNullOrWhiteSpace(trimContent))
            {
                return value.TrimStart();
            }
            trimContent = Regex.Replace(trimContent, @"([.$\\^*+?\[\]()])", @"\$1");
            string limit = isRepeat ? "+" : "";
            string regexString = $"^(?:{trimContent}){limit}";
            value = Regex.Replace(value, regexString, "");
            return value;
        }

        /// <summary>
        /// 移除字符串结尾指定的字符
        /// </summary>
        /// <param name="value">字符串本身</param>
        /// <param name="trimContent">将要在字符串结尾移除的字符</param>
        /// <param name="isRepeat">是否允许重复匹配</param>
        /// <returns></returns>
        public static string TrimEnd(this string value, string trimContent = "", bool isRepeat = true)
        {
            if (string.IsNullOrWhiteSpace(value))
            {
                return "";
            }
            if (string.IsNullOrWhiteSpace(trimContent))
            {
                return value.Trim();
            }
            trimContent = Regex.Replace(trimContent, @"([.$\\^*+?\[\]()])", @"\$1");
            string limit = isRepeat ? "+" : "";
            string regexString = $"(?:{trimContent}){limit}$";
            value = Regex.Replace(value, regexString, "");
            return value;
        }

        /// <summary>
        ///通过指定正则表达式替换指定内容
        /// </summary>
        /// <param name="value">字符串本身</param>
        /// <param name="pattern">正则表达式</param>
        /// <param name="replacement">替换内容</param>
        /// <returns></returns>
        public static string Replace(this string value, string pattern, string replacement = "")
        {
            if (string.IsNullOrWhiteSpace(value))
            {
                return "";
            }
            if (string.IsNullOrWhiteSpace(pattern))
            {
                throw new ArgumentException("pattern 不能为空");
            }
            value = Regex.Replace(value, pattern, replacement);
            return value;
        }
        /// <summary>
        /// 通过指定正则表达式替换指定内容
        /// </summary>
        /// <param name="value">字符串本身</param>
        /// <param name="pattern">正则表达式</param>
        /// <param name="replacement">替换内容</param>
        /// <param name="options">正则表达式选项</param>
        /// <returns></returns>
        public static string Replace(this string value, string pattern, string replacement, RegexOptions options)
        {
            if (string.IsNullOrWhiteSpace(value))
            {
                return "";
            }
            if (string.IsNullOrWhiteSpace(pattern))
            {
                throw new ArgumentException("pattern 参数不能为空");
            }
            value = Regex.Replace(value, pattern, replacement, options);
            return value;
        }

    }

字符串扩展使用


        static void Main(string[] args)
        {
            string original = "NameNameemaNHello Name World!NameName";
            Console.WriteLine($"原字符串:{original}\r\n");
            Console.WriteLine($"移除指定字符串前后Name并重复匹配:{original.Trim("Name")}");
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine($"移除指定字符串前后Name单次匹配:{original.Trim("Name", false)}");
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine($"移除字符串开始Name并重复匹配:{original.TrimStart("Name")}");
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine($"移除字符串开始Name不重复匹配:{original.TrimStart("Name", false)}");
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine($"移除字符串结束Name并重复匹配:{original.TrimEnd("Name")}");
            Console.WriteLine(Environment.NewLine);
            Console.WriteLine($"移除字符串结束Name不重复匹配:{original.TrimEnd("Name", false)}");
            Console.Read();
        }

C#使用正则表达式移除字串符前后指定的字符串-第0张图片



转载请注明:清风亦平凡 » C#使用正则表达式移除字串符前后指定的字符串

]]>
https://www.mlplus.net/2022/04/04/csharp-regex-trim-remove/feed/ 0
.NET 5中使用GB2312编码报错的问题 https://www.mlplus.net/2021/12/17/net-core-register-encoding/ https://www.mlplus.net/2021/12/17/net-core-register-encoding/#respond Fri, 17 Dec 2021 02:53:22 +0000 https://www.mlplus.net/?p=3702 背景

在使用.net 5构建应用时,在处理某些编码问题使用了GB2312,应用运行后报错。经过确认知道了编码 GB2312 默认不支持。

异常信息

Not Support Encoding’GB2312′ is not a supported encoding name. For information on defining a custom encoding, see the documentation for the Encoding.RegisterProvider method. (Parameter ‘name’)
System.Exception: Not Support Encoding’GB2312′ is not a supported encoding name. For information on defining a custom encoding, see the documentation for the Encoding.RegisterProvider method. (Parameter ‘name’)

解决办法

添加引用包

.NET 5中使用GB2312编码报错的问题-第0张图片
System.Text.Encoding.CodePages

注册

在使用System.Text.Encoding.GetEncoding ("GB2312")之前,在代码中执行注册

 System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);



转载请注明:清风亦平凡 » .NET 5中使用GB2312编码报错的问题

]]>
https://www.mlplus.net/2021/12/17/net-core-register-encoding/feed/ 0
.Net访问Windows共享目录 https://www.mlplus.net/2021/06/29/net-access-shared-folder/ https://www.mlplus.net/2021/06/29/net-access-shared-folder/#respond Tue, 29 Jun 2021 01:00:00 +0000 https://www.mlplus.net/?p=3538 背景

无意见又翻到了曾经的一些项目,看到了关于.Net访问Windows共享目录的一些代码。曾经在内网通过共享目录作为服务器文件的存储方式,个人觉得部分的实现代码可以记录下来作为备份。所以,整理了以下代码。

代码实现

公用内容


    /// <summary>
    ///  IdentityScope 的摘要说明
    /// </summary>
    public class IdentityScope : IDisposable
    {
        // obtains user token
        [DllImport("advapi32.dll", SetLastError = true)]
        static extern bool LogonUser(string pszUsername, string pszDomain, string pszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

        // closes open handes returned by LogonUser

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]

        extern static bool CloseHandle(IntPtr handle);


        [DllImport("Advapi32.DLL")]
        static extern bool ImpersonateLoggedOnUser(IntPtr hToken);

        [DllImport("Advapi32.DLL")]
        static extern bool RevertToSelf();

        // logon types
        const int LOGON32_LOGON_INTERACTIVE = 2;
        const int LOGON32_LOGON_NETWORK = 3;
        const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
        // logon providers
        const int LOGON32_PROVIDER_DEFAULT = 0;
        const int LOGON32_PROVIDER_WINNT50 = 3;
        const int LOGON32_PROVIDER_WINNT40 = 2;
        const int LOGON32_PROVIDER_WINNT35 = 1;

        private bool disposed;

        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="sUsername">用户名</param>
        /// <param name="sDomain">第二个参数是域名,有域名的话写域名,没有域名写目标机器的IP·</param>
        /// <param name="sPassword">密码</param>
        public IdentityScope(string sUsername, string sDomain, string sPassword)
        {

            // initialize tokens
            IntPtr pExistingTokenHandle = new IntPtr(0);
            IntPtr pDuplicateTokenHandle = new IntPtr(0);
            try
            {

                // get handle to token
                bool bImpersonated = LogonUser(sUsername, sDomain, sPassword, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref pExistingTokenHandle);
                if (bImpersonated)
                {
                    if (!ImpersonateLoggedOnUser(pExistingTokenHandle))
                    {
                        int nErrorCode = Marshal.GetLastWin32Error();
                        // throw new Exception("ImpersonateLoggedOnUser error;Code=" + nErrorCode);
                        throw new Exception("文件服务器远程连接异常,请稍后再试!");
                    }

                }
                else
                {
                    int nErrorCode = Marshal.GetLastWin32Error();
                    //throw new Exception("LogonUser error;Code=" + nErrorCode);
                    throw new Exception("文件服务器远程连接异常,请稍后再试!");

                }

            }
            finally
            {
                // close handle(s)
                if (pExistingTokenHandle != IntPtr.Zero)
                {

                    CloseHandle(pExistingTokenHandle);
                }

                if (pDuplicateTokenHandle != IntPtr.Zero)
                {

                    CloseHandle(pDuplicateTokenHandle);
                }
            }

        }

        protected virtual void Dispose(bool disposing)
        {

            if (!disposed)
            {
                RevertToSelf();
                disposed = true;
            }

        }

        public void Dispose()
        {
            Dispose(true);
        }
    }

访问调用

      
using ShareAccess.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ShareAccess
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
               
                using (IdentityScope c = new IdentityScope("Administrator","192.168.0.102","1234567"))
                {
                    FileInfo fileInfo = new FileInfo("C:\\Users\\finder\\Desktop\\taskapp2.zip");
                    fileInfo.CopyTo("\\\\192.168.0.102\\Share\\" + fileInfo.Name);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"{ex}");
            }
            Console.Read();
        }
    }
}
.Net访问Windows共享目录-第0张图片

示例下载

.Net访问Windows共享目录示例



转载请注明:清风亦平凡 » .Net访问Windows共享目录

]]>
https://www.mlplus.net/2021/06/29/net-access-shared-folder/feed/ 0
System.Data.OracleClient需要Oracle 客户端软件version 8.1.7或更高版本 https://www.mlplus.net/2021/06/23/system-data-oracle-client-error/ https://www.mlplus.net/2021/06/23/system-data-oracle-client-error/#respond Wed, 23 Jun 2021 10:45:48 +0000 https://www.mlplus.net/?p=3541 背景

因业务需求需要进行数据同步,客户又没有提供相关的接口。经过协商,客户提供相关视图直连他们数据库。

数据库:Oracle 11g

问题

使用.net访问Oracle数据出现以下错误:

System.Data.OracleClient 需要Oracle 客户端软件 version 8.1.7 或 更高版本

System.Data.OracleClient需要Oracle 客户端软件version 8.1.7或更高版本-第0张图片

解决

  • 找到Oracle安装文件夹找到oci.dlloraociei11.dll两个dll文件
  • 将以上两个文件放到system32目录下或者程序的bin目录下

下载

Oracle_11g提取码:hrbc
以上下载地址中的文件就是本文中提到的两个DLL文件



转载请注明:清风亦平凡 » System.Data.OracleClient需要Oracle 客户端软件version 8.1.7或更高版本

]]>
https://www.mlplus.net/2021/06/23/system-data-oracle-client-error/feed/ 0
.NET读取Pem证书私钥解密 https://www.mlplus.net/2021/06/14/dot-net-rsa-pem/ https://www.mlplus.net/2021/06/14/dot-net-rsa-pem/#respond Mon, 14 Jun 2021 03:57:53 +0000 https://www.mlplus.net/?p=3532 背景

因业务需求的需要,要与第三方进行相关的数据对接,按照第三方规定需要对其数据通过私钥进行RSA解密。第三方提供了相关的Pem证书文件,但是这种格式的证书文件.NET并不能直接使用,需要进行相关的转换。

pem在转XML时需要依赖一个第三方库BouncyCastle

下载

BouncyCastle
如果通过官网下载比较慢,可以使用本博以上链接下载。

代码实现


using System;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;

namespace CommonTool.Share.Util
{
    /// <summary>
    /// 非对称RSA解密
    /// </summary>
    public class PemRsaEncrypt
    {
       /// <summary>
       /// RSA解密
       /// </summary>
       /// <param name="filePath">pem私钥证书路径</param>
       /// <param name="content">加密的文本</param>
       /// <returns>解密后的明文</returns>
       public static string Decrypt(string filePath,string content)
       {
           using (var fileStream = File.OpenText(filePath))
           {
               string privateJavaKey = fileStream.ReadToEnd().Replace("-----BEGIN PRIVATE KEY-----", "").Replace("-----END PRIVATE KEY-----", "").Replace("\r", "").Replace("\n", "");
               RsaPrivateCrtKeyParameters privateKeyParam = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(privateJavaKey));
               string xmlPrivateKey = string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent><P>{2}</P><Q>{3}</Q><DP>{4}</DP><DQ>{5}</DQ><InverseQ>{6}</InverseQ><D>{7}</D></RSAKeyValue>",
                            Convert.ToBase64String(privateKeyParam.Modulus.ToByteArrayUnsigned()),
                            Convert.ToBase64String(privateKeyParam.PublicExponent.ToByteArrayUnsigned()),
                            Convert.ToBase64String(privateKeyParam.P.ToByteArrayUnsigned()),
                            Convert.ToBase64String(privateKeyParam.Q.ToByteArrayUnsigned()),
                            Convert.ToBase64String(privateKeyParam.DP.ToByteArrayUnsigned()),
                            Convert.ToBase64String(privateKeyParam.DQ.ToByteArrayUnsigned()),
                            Convert.ToBase64String(privateKeyParam.QInv.ToByteArrayUnsigned()),
                            Convert.ToBase64String(privateKeyParam.Exponent.ToByteArrayUnsigned()));

               RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
               byte[] cipherbytes;
               rsa.FromXmlString(xmlPrivateKey);
               cipherbytes = rsa.Decrypt(Convert.FromBase64String(content), false);
               return Encoding.UTF8.GetString(cipherbytes);
           }
       }
    }
}



转载请注明:清风亦平凡 » .NET读取Pem证书私钥解密

]]>
https://www.mlplus.net/2021/06/14/dot-net-rsa-pem/feed/ 0
移除.net解决方案中TFS的绑定控制 https://www.mlplus.net/2021/03/26/removenetcodetfs/ https://www.mlplus.net/2021/03/26/removenetcodetfs/#respond Fri, 26 Mar 2021 11:23:56 +0000 https://www.mlplus.net/?p=3396 TFS与解决方案绑定信息清理

tfs与解决方案之间有相关信息需要清理,以下是相关的清理步骤。

删除关联文件以及文件夹

删除项目目录下所有的*.vssscc*.vspscc为后缀的文件,删除隐藏文件夹$tf

修改项目的解决方案文件

在目录中找到以*.sln为后缀名的解决方案文件,打开文件进行编辑。删除TeamFoundationVersionControl所在的整块内容并保存。

GlobalSection(TeamFoundationVersionControl) = preSolution
SccNumberOfProjects = 2
SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C}
SccTeamFoundationServer = http:///tfs/defaultcollection
SccLocalPath0 = .
SccProjectUniqueName1 = .csproj
//Scc……
EndGlobalSection

修改项目文件

在项目目录中,找到以*.csproj为后缀的项目文件,打开进行编辑。

删除<SccProjectName><SccLocalPath><SccAuxPath><SccProvider>这四个节点。

代码实现

为了方便以后使用,所以这里使用.net 5 写一个小工具来清理这些TFS绑定信息。

移除.net解决方案中TFS的绑定控制-第0张图片


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace DisassociateSourceCodeManagementTFS
{
    public partial class DisassociateTFS : Form
    {
        
        public DisassociateTFS()
        {
            InitializeComponent();
        }
        private void btnDisassociateTFS_Click(object sender, EventArgs e)
        {
            string path = txtSourceFolder.Text;
            if (string.IsNullOrWhiteSpace(path))
            {
                MessageBox.Show("请选择需要解除TFS绑定的源码目录","提示",MessageBoxButtons.OK,MessageBoxIcon.Information);
                return;
            }
            if (!Directory.Exists(path))
            {
                return;
            }
            btnDisassociateTFS.Enabled = false;
            var vssscc = Directory.EnumerateFiles(path, "*.vssscc", SearchOption.AllDirectories);
            var vspscc = Directory.EnumerateFiles(path, "*.vspscc", SearchOption.AllDirectories);
            DeleteFolderOrFiles(vssscc);
            DeleteFolderOrFiles(vspscc);
            var sln = Directory.EnumerateFiles(path, "*.sln", SearchOption.AllDirectories);
            RewriteFileContent(sln,(item)=> { 
               return Regex.Replace(item, @"GlobalSection\(TeamFoundationVersionControl\)[\s\S]+?EndGlobalSection", "");
            });
            var csproj = Directory.EnumerateFiles(path, "*.csproj", SearchOption.AllDirectories);
            RewriteFileContent(csproj, (item) => {
                item = Regex.Replace(item, @"<SccProjectName>.+?</SccProjectName>", "");
                item = Regex.Replace(item, @"<SccLocalPath>.+?</SccLocalPath>", "");
                item = Regex.Replace(item, @"<SccAuxPath>.+?</SccAuxPath>", "");
                item = Regex.Replace(item, @"<SccProvider>.+?</SccProvider>", "");
                return item;
            });
            var tf = Directory.EnumerateDirectories(path, "$tf", SearchOption.AllDirectories);
            DeleteFolderOrFiles(tf);
            btnDisassociateTFS.Enabled = true;
            MessageBox.Show("解除TFS绑定的源码目录成功", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void DeleteFolderOrFiles(IEnumerable<string> files)
        {
            if (files == null)
            {
                return;
            }
            foreach (var item in files)
            {
                if (File.Exists(item))
                {
                    File.Delete(item);
                    continue;
                }
                if (Directory.Exists(item))
                {
                    Directory.Delete(item, true);
                }
            }
        }
        private void RewriteFileContent(IEnumerable<string> files,Func<string,string> contentOperation)
        {
            foreach (var item in files)
            {
                if (!File.Exists(item))
                {
                    continue;
                }
                Encoding textEncoding = GetFileEncodeType(item);
                StreamReader reader = new StreamReader(item, textEncoding);
                string fileContent = reader.ReadToEnd();
                reader?.Close();
                reader?.Dispose();
                if (contentOperation != null)
                {
                    fileContent = contentOperation(fileContent);
                }
                StreamWriter writer = new StreamWriter(item, false, textEncoding);
                writer.Write(fileContent);
                writer.Flush();
                writer?.Close();
                writer?.Dispose();
            }
        }
        /// <summary>
        /// 获取指定文件的编码
        /// 以防止在不知道文件编码格式的情况下处理文件而造成的乱码问题
        /// </summary>
        /// <param name="filename">文件路径</param>
        /// <returns></returns>
        private System.Text.Encoding GetFileEncodeType(string filename)
        {
            if (!File.Exists(filename))
            {
                return System.Text.Encoding.Default;
            }
            System.Text.Encoding ReturnReturn = null;
            System.IO.FileStream fs = null;
            System.IO.BinaryReader br = null;
            try
            {
                fs = new System.IO.FileStream(filename, System.IO.FileMode.Open, System.IO.FileAccess.Read);
                br = new System.IO.BinaryReader(fs);
                byte[] buffer = br.ReadBytes(2);
                if (buffer.Length > 0 && buffer[0] >= 0xEF)
                {
                    if (buffer[0] == 0xEF && buffer[1] == 0xBB)
                    {
                        ReturnReturn = System.Text.Encoding.UTF8;
                    }
                    else if (buffer[0] == 0xFE && buffer[1] == 0xFF)
                    {
                        ReturnReturn = System.Text.Encoding.BigEndianUnicode;
                    }
                    else if (buffer[0] == 0xFF && buffer[1] == 0xFE)
                    {
                        ReturnReturn = System.Text.Encoding.Unicode;
                    }
                    else
                    {
                        ReturnReturn = System.Text.Encoding.Default;
                    }
                }
                else if (buffer.Length > 0 && buffer[0] == 0xe4 && buffer[1] == 0xbd) //无BOM的UTF-8 
                {
                    ReturnReturn = System.Text.Encoding.UTF8;
                }
                else
                {
                    ReturnReturn = System.Text.Encoding.Default;
                }
            }
            catch
            {
                ReturnReturn = System.Text.Encoding.Default;
            }
            finally
            {
                br?.Close();
                fs?.Close();
                fs?.Dispose();
            }
            return ReturnReturn;
        }
        private void btnOpenFloder_Click(object sender, EventArgs e)
        {
            if (fbdFolderSelect.ShowDialog() == DialogResult.OK)
            {
                txtSourceFolder.Text = fbdFolderSelect.SelectedPath;
            }
        }
        private void txtSourceFolder_DragDrop(object sender, DragEventArgs e)
        {
            string path = ((System.Array)e.Data.GetData(DataFormats.FileDrop)).GetValue(0).ToString();
            if (File.Exists(path))
            {
                FileInfo fileInfo = new FileInfo(path);
                path=fileInfo.DirectoryName;
            }
            txtSourceFolder.Text = path;
        }
        private void txtSourceFolder_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                e.Effect = DragDropEffects.All;//重要代码:表明是所有类型的数据,比如文件路径
            }
            else
            {
                e.Effect = DragDropEffects.None;
            }

        }
    }
}

示例下载

解除解决方案与TFS绑定
以上示例请使用Microsoft Visual Studio 2019 打开



转载请注明:清风亦平凡 » 移除.net解决方案中TFS的绑定控制

]]>
https://www.mlplus.net/2021/03/26/removenetcodetfs/feed/ 0
ASP.NET Core Web Api实现大文件切/分片上传 https://www.mlplus.net/2020/12/31/asp-net-core-big-file-uploader/ https://www.mlplus.net/2020/12/31/asp-net-core-big-file-uploader/#respond Thu, 31 Dec 2020 08:15:58 +0000 https://www.mlplus.net/?p=3313 一年前的一个项目,有人反馈上传超时,超时原因是文件大小超出限制。由于原来维护项目的人员离开,现在上传超时的问题就有我来处理。解决方案也比较简单,就是切片上传。前端不想自己写了,就利用了上传组件,这个上传组件是百度WebUploaderWebUploader这个组件被使用的也比较广泛,为了省事就用它啦!

上传中

ASP.NET Core Web Api实现大文件切/分片上传-第0张图片
ASP.NET Core Web Api实现大文件切/分片上传-第1张图片

合并后

ASP.NET Core Web Api实现大文件切/分片上传-第2张图片

代码实现

前端代码

前端上传以及分片使用的百度上传组件WebUploader


<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Home Page - WebUpload</title>

    
        <link rel="stylesheet" href="/lib/bootstrap/dist/css/bootstrap.css" />
    
    
    <link rel="stylesheet" href="/css/site.css" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" href="/">WebUpload</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" href="/">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" href="/Home/Privacy">Privacy</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        


        <main role="main" class="pb-3">
            <link rel="stylesheet" type="text/css" href="/webuploader/webuploader.css">
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
    <script src="/webuploader/webuploader.js"></script>
    <div class="text-center">
        <h1 class="display-4">Welcome</h1>
        <div id="uploader" class="wu-example">
            <div class="container-fluid">
                <div class="col-md-10">
                    <div class="row">文件上传示例:</div>
                    <div class="row">
                        <div id="uploader" class="wu-example">
                            <!--用来存放文件信息-->
                            <div id="fileList" class="uploader-list"></div>
                            <div class="btns">
                                <div id="picker" class="btn btn-primary">选择文件</div>

                            </div>

                        </div>
                    </div>

                    <div class="row">

                    </div>
                    <div class="row">  <button id="ctlBtn" class="btn btn-default">开始上传</button></div>
                </div>
                <div>
                </div>
            </div>
        </div>
        <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
    </div>
<script>
    jQuery(function () {
        var $ = jQuery,
            $list = $('#fileList'),
            $btn = $('#ctlBtn'),
            state = 'pending',
            uploader, guid = {};
        uploader = WebUploader.create({
            // 不压缩image
            resize: false,

            // swf文件路径
            swf: "~/webuploader/Uploader.swf",

            server: 'http://localhost:8011/api/File/UplaodFile?isConvert=true',

            // 选择文件的按钮。可选。
            // 内部根据当前运行是创建,可能是input元素,也可能是flash.
            pick: '#picker',
            chunked: true

        });
        
        // 当有文件添加进来的时候
        uploader.on('fileQueued', function (file) {
            guid[file.id] = +(new Date());
            $list.append('<div id="' + file.id + '" class="item">' +
                '<h4 class="info">' + file.name + '</h4>' +
                '<p class="state">等待上传...</p>' +
                '</div>');
            uploader
                .md5File(file)
                .progress(function (percentage) {
                    // console.log("Percentage:", percentage);
                    $("#ctlBtn").hide();
                })
                // MD5计算完毕,可以点击上传了
                .then(function (fileMd5) {
                    file.fileMd5 = fileMd5;
                    $("#ctlBtn").show()
                    console.log(fileMd5);
                });

        });
        uploader.on('uploadBeforeSend', function (file, data, headers) {
            $.extend(data, { "fileId": guid[data.id], "fileMd5": file.file.fileMd5 });
            $.extend(headers, {
                "Authorization":"Bearer "
            });
            if (data.chunks) {
                data["chunkStart"] = file.start;
                data["chunkEnd"] = file.end;
            }
        });
        // 文件上传过程中创建进度条实时显示。
        uploader.on('uploadProgress', function (file, percentage) {

            var $li = $('#' + file.id),
                $percent = $li.find('.progress .progress-bar');
            // 避免重复创建
            if (!$percent.length) {
                $percent = $('<div class="progress progress-striped active">' +
                    '<div class="progress-bar" role="progressbar" style="width: 0%">' +
                    '</div>' +
                    '</div>').appendTo($li).find('.progress-bar');
            }
            $li.find('p.state').text('上传中');
            $percent.css('width', percentage * 100 + '%');

        });

        uploader.on('uploadSuccess', function (file) {
            $('#' + file.id).find('p.state').text('已上传');
        });

        uploader.on('uploadError', function (file) {
            $('#' + file.id).find('p.state').text('上传出错');
        });

        uploader.on('uploadComplete', function (file) {
            $('#' + file.id).find('.progress').fadeOut();
        });
        uploader.on('all', function (type) {
            if (type === 'startUpload') {
                state = 'uploading';
            } else if (type === 'stopUpload') {
                state = 'paused';
            } else if (type === 'uploadFinished') {
                state = 'done';
            }
            if (state === 'uploading') {
                $btn.text('暂停上传');
            } else {
                $btn.text('开始上传');
            }

        });

        $btn.on('click', function () {
            if (state === 'uploading') {
                uploader.stop();
            } else {
                uploader.upload();
            }
        });
    });
</script>

        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2020 - WebUpload - <a href="/Home/Privacy">Privacy</a>
        </div>
    </footer>

    
        <script src="/lib/jquery/dist/jquery.js"></script>
        <script src="/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
    
    
    <script src="/js/site.js?v=4q1jwFhaPaZgr8WAUSrux6hAuh0XDg9kPS3xIVq36I0"></script>

    
</body>
</html>

后台实现

后台使用的是ASP .NET Core Web API,实现方式也很简单。文件的校验,分块的校验等这里就省略了。
    
    [Route("api/[controller]")]
    public class FileController : Controller
    {
        public static readonly object locker = new object();
        [HttpPost]
        [Route("UplaodFile")]
        public IActionResult UplaodFile([FromForm] IFormCollection form)
        {
            try
            {
                string fileId = form["fileId"];
                if (string.IsNullOrWhiteSpace(fileId)||form.Files==null||form.Files.Count==0)
                {
                    return BadRequest();
                }
                string tempFileFolder = Path.Combine(AppContext.BaseDirectory, $"FileUploader/{fileId}");
                CreateFolder(tempFileFolder);
                if (form.ContainsKey("chunks"))
                {
                    string chunksString = form["chunks"];
                    int chunks = int.Parse(chunksString);
                    string chunkString = form["chunk"];
                    int chunk = int.Parse(chunkString);
                    string sizeString = form["size"];
                    long size = long.Parse(sizeString);
                    string chunkStartString = form["chunkStart"];
                    string chunkEndString = form["chunkEnd"];
                    string chunkMd5 = form["chunkMd5"];
                    string fileMd5 = form["fileMd5"];
                    string lastModifiedDate = form["lastModifiedDate"];
                    var file = form.Files.FirstOrDefault();
                    string name = file.FileName;
                    string ext = Path.GetExtension(name);
                    Stream stream = file.OpenReadStream();
                    byte[] bytes = new byte[stream.Length];
                    stream.Read(bytes, 0, bytes.Length);
                    string factFilePath = Path.Combine(tempFileFolder, $"{name}");
                    string chunkFilePath = $"{factFilePath}.chunk{chunk}";
                    string chunkFileTempPath = $"{chunkFilePath}.temp";
                    System.IO.File.Delete(chunkFileTempPath);
                    FileStream fs = new FileStream(chunkFileTempPath, FileMode.Create);
                    BinaryWriter bw = new BinaryWriter(fs);
                    bw.Write(bytes);
                    bw.Flush();
                    bw.Close();
                    bw.Dispose();
                    fs.Close();
                    fs.Dispose();
                    stream.Close();
                    stream.Dispose();
                    System.IO.File.Move(chunkFileTempPath, chunkFilePath);
                    bool isMerge = true;
                    for (int i = 0; i < chunks; i++)
                    {
                        if (!System.IO.File.Exists($"{factFilePath}.chunk{i}"))
                        {
                            isMerge = false;
                            break;
                        }
                    }

                    if (isMerge)
                    {
                        lock (locker)
                        {
                            if (isMerge)
                            {
                                if (System.IO.File.Exists(factFilePath))
                                {
                                    return Ok();
                                }
                                var fileStream = new FileStream(factFilePath, FileMode.Create);
                                for (int i = 0; i < chunks; i++)
                                {
                                    string chunkFile = $"{factFilePath}.chunk{i}";
                                    var chunkBytes = System.IO.File.ReadAllBytes(chunkFile);
                                    fileStream.Write(chunkBytes, 0, chunkBytes.Length);
                                    fileStream.Flush();
                                    System.IO.File.Delete(chunkFile);//删除分块
                                }
                                fileStream.Close();
                                fileStream.Dispose();
                            }
                        }

                    }
                }
                else
                {
                    var file = form.Files.FirstOrDefault();
                    Stream stream = file.OpenReadStream();
                    byte[] bytes = new byte[stream.Length];
                    stream.Read(bytes, 0, bytes.Length);
                    FileStream fileStream = new FileStream(Path.Combine(tempFileFolder, $"{file.FileName}"), FileMode.Create);
                    BinaryWriter bw = new BinaryWriter(fileStream);
                    bw.Write(bytes);
                    bw.Flush();
                    bw.Close();
                    bw.Dispose();
                    fileStream.Close();
                    fileStream.Dispose();
                    stream.Close();
                    stream.Dispose();
                }
            }
            catch (Exception ex)
            {
                Console.Write(ex);
            }

            return Ok();
        }
        private void CreateFolder(string folderPath)
        {
            if (!Directory.Exists(folderPath))
            {
                Directory.CreateDirectory(folderPath);
            }
        }
        [HttpGet]
        [Route("Index")]
        public IActionResult Index()
        {
            return Ok();
        }
    }

示例下载

.NET CORE大文件分/切片上传示例



转载请注明:清风亦平凡 » ASP.NET Core Web Api实现大文件切/分片上传

]]>
https://www.mlplus.net/2020/12/31/asp-net-core-big-file-uploader/feed/ 0
.NET操作达梦数据库编译错误处理 https://www.mlplus.net/2020/12/07/dmprovidererrorprocess/ https://www.mlplus.net/2020/12/07/dmprovidererrorprocess/#respond Mon, 07 Dec 2020 02:03:58 +0000 https://www.mlplus.net/?p=3279 由于客户突然要进行国产化的一个要求,数据库使用更换了国产达梦数据库,将数据由Oracle数据库迁移到达梦数据库。所以,之前基于Oracle处理的一些功能就需要进行一些调整。

使用NuGet 引入达梦数据提供器DmProvider 

.NET操作达梦数据库编译错误处理-第0张图片

引用相关的组件后,就按照套路对已经存在的方法进行相关调整,结果出现了一些编译出错且无论如何调整都无法消除。如下:

错误 CS1705 标识为“Dm, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null”的程序集“Dm”所使用的“System.Runtime, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a”版本高于所引用的标识为“System.Runtime, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a”的程序集“System.Runtime”

.NET操作达梦数据库编译错误处理-第1张图片

通过各种搜索引擎查找一通,最后也没有见到任何处理问题的方法。总觉得国产的某些东西,距大面积商用还有很远的距离。实在没有办法了,就到达梦官方把达梦数据库Windows开发版本下载下来,并加入达梦数据库官方QQ支持群,咨询关于.net framework 4.0的数据库驱动哪里有提供。

.NET操作达梦数据库编译错误处理-第2张图片

根据QQ群的信息回复,在安装目录找到了相关的组件并引用进行尝试,最终错误消失。

.NET操作达梦数据库编译错误处理-第3张图片

至于为什么Nuget上的会出现异常,也没有深入研究。感觉这没有意义!!



转载请注明:清风亦平凡 » .NET操作达梦数据库编译错误处理

]]>
https://www.mlplus.net/2020/12/07/dmprovidererrorprocess/feed/ 0
NET CORE将对象属性按照指定顺序输出 https://www.mlplus.net/2020/09/08/net-core-object-attribute-sort-order/ https://www.mlplus.net/2020/09/08/net-core-object-attribute-sort-order/#respond Tue, 08 Sep 2020 08:42:31 +0000 https://www.mlplus.net/?p=3194 背景

因业务需求,需要与第三方进行融合登录。第三方融合登录接口有关于验签的要求,将解密的数据按照规定的顺序进行MD5进行签名验证并与提供的MD5签名作为对比。其加密的明文是Json字符串,解密后还原后要按照URl参数的形式进行排列进行MD5签名。

规定

加密参数(注:加密参数名称均为小写字母,没有使用驼峰法命名)

名称类型默认值简介
sourceidString必传来源标识
targetidString必传目标标识
usercodeString必传用户唯一标识
usernameString用户姓名
idcardString身份证号码
phoneString手机号码
ounameString部门名称
timeString必传当前时间戳,精确到毫秒

签名数据采用以上面表格顺序以及以下格式进行字段拼接

sourceid=skyfinder&targetid=sky&usercode=100001&username=清风&idcard=110101199003074194&phone=16620927111&ouname=博客&time=1599539358

实现

创建一个自定义特性SortOrderAttribute,用来标识实体属性顺序与名称。


public class SortOrderAttribute: Attribute
 {
        public string Name { get; set; }

        public int Index { get; set; }

        public SortOrderAttribute(string name,int index)
        {
            Name = name;
            Index = index;
        }
    }

根据表格内容创建实体模型指定其排列顺序并通过方法GetUrIParameterString实现顺序输出内容。


public class PoliceStudiesUser
{
        /// <summary>
        /// 来源标识
        /// </summary>
        [SortOrder("来源标识",1)]
        public string sourceid { get; set; }
        /// <summary>
        /// 目标标识
        /// </summary>
        [SortOrder("来源标识", 2)]
        public string targetid { get; set; }
        /// <summary>
        /// 用户唯一标识
        /// </summary>
        [SortOrder("来源标识", 3)]
        public string usercode { get; set; }
        /// <summary>
        /// 用户姓名
        /// </summary>
        [SortOrder("来源标识", 4)]
        public string username { get; set; }
        /// <summary>
        /// 身份证号码
        /// </summary>
        [SortOrder("来源标识", 5)]
        public string idcard { get; set; }
        /// <summary>
        /// 手机号码
        /// </summary>
        [SortOrder("来源标识", 6)]
        public string phone { get; set; }
        /// <summary>
        /// 部门名称
        /// </summary>
        [SortOrder("来源标识", 7)]
        public string ouname { get; set; }

        /// <summary>
        /// 当前时间戳,精确到毫秒
        /// </summary>
        [SortOrder("来源标识", 8)]
        public string time { get; set; }

        public string GetUrIParameterString()
        {
            List<string> urlParameter = new List<string>();
            //foreach (PropertyInfo item in this.GetType().GetProperties().OrderBy(p => (p.GetCustomAttributes(true)[0] as SortOrderAttribute).Index).ToArray())
            Type type = this.GetType();
            var properties = type.GetProperties().OrderBy(p => (p.GetCustomAttributes(true)[0] as SortOrderAttribute).Index).ToArray();
            foreach (var item in properties)
            {
                string value = item.GetValue(this, null) == null ? "" : item.GetValue(this, null).ToString().Trim();
                if (string.IsNullOrWhiteSpace(value))
                {
                    continue;
                }
                urlParameter.Add($"{item.Name}={value}");
            }
            return string.Join("&",urlParameter);
        }
    }

调用测试

       

 static void Main(string[] args)
 {
            PoliceStudiesUser studiesUser = new PoliceStudiesUser() {
                sourceid="skyfinder",
                targetid="sky",
                idcard= "110101199003074194",
                usercode="100001",
                username="清风",
                ouname="博客",
                phone= "16620927111",
                time= "1599539358"
            };
            Console.WriteLine(studiesUser.GetUrIParameterString());
            Console.Read();
 }
NET CORE将对象属性按照指定顺序输出-第0张图片

示例下载

NET CORE指定顺序输出对象属性内容



转载请注明:清风亦平凡 » NET CORE将对象属性按照指定顺序输出

]]>
https://www.mlplus.net/2020/09/08/net-core-object-attribute-sort-order/feed/ 0
.NET CORE实现MD5加密 https://www.mlplus.net/2020/09/03/net-core-md5/ https://www.mlplus.net/2020/09/03/net-core-md5/#respond Wed, 02 Sep 2020 16:00:00 +0000 https://www.mlplus.net/?p=3154 背景

因某些需求,需要与合作单位进行相应的数据交互,而交互均使用了加密方式处理并用MD5作为签名,以便校验传输内容是否遭到篡改。

MD5

md5是一种信息摘要算法,它可以从一个字符串或一个文件中按照一定的规则生成一个特殊的字符串,并且一个文件所对应的MD5摘要是固定的,当文件内容变化后,其MD5值也会不一样,因此,在应用中经常使用MD5值来验证一段数据有没有被篡改。

.NET CORE 实现MD5

以下简单实现,支持返回大小写、32位以及16位MD5字符串。


    public static class EncryptionMd5
    {
         /// <summary>
        /// md5加密
        /// </summary>
        /// <param name="content">要加密的内容</param>
        /// <param name="isUpper">是否大写,默认小写</param>
        /// <param name="is16">是否是16位,默认32位</param>
        /// <returns></returns>
        public static string Md5(string content,bool isUpper=false,bool is16=false)
        {
            using (var md5 = MD5.Create())
            {
                var result = md5.ComputeHash(Encoding.UTF8.GetBytes(content));
                string md5Str = BitConverter.ToString(result);
                md5Str = md5Str.Replace("-", "");
                md5Str= isUpper ? md5Str : md5Str.ToLower();
                return is16 ? md5Str.Substring(8,16) : md5Str;
            }
        }
    }
以上是使用.NET CORE自身提供的MD5加密进行实现,如果想了解原理以及具体实现方式,请使用万能搜索引擎查找。

使用方式


using System;
using System.Text;
using System.Security.Cryptography;
namespace CryptographyApplication
{
   class SecurityCryptography
   {
      static void Main(string[] args)
      {
         Console.WriteLine(EncryptionMd5.Md5("Hello World!"));
         Console.ReadKey();
      }
   }
}
.NET CORE实现MD5加密-第0张图片



转载请注明:清风亦平凡 » .NET CORE实现MD5加密

]]>
https://www.mlplus.net/2020/09/03/net-core-md5/feed/ 0
.Net Core实现AES加解密 https://www.mlplus.net/2020/09/02/net-core-encryption-aes/ https://www.mlplus.net/2020/09/02/net-core-encryption-aes/#respond Wed, 02 Sep 2020 04:30:29 +0000 https://www.mlplus.net/?p=3148 背景

因业务需求,需要与其他平台实现融合登录,根据提供融合登录方的文档内容,对AES加密解密相关内容使用.NET CORE来进行一次实现。以下记录实现AES加解密后的内容,便以后备用。

AES

高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。严格地说,AESRijndael加密法并不完全一样(虽然在实际应用中二者可以互换),因为Rijndael加密法可以支持更大范围的区块和密钥长度:AES的区块长度固定为128 比特,密钥长度则可以是128192256比特;而Rijndael使用的密钥和区块长度可以是32位的整数倍,以128位为下限,256比特为上限。包括AES-ECBAES-CBCAES-CTRAES-OFBAES-CFB

.NET CORE 的AES加解密实现


    public enum CiphertextType
    {
        /// <summary>
        /// Base64
        /// </summary>
        Base64 = 1,
        /// <summary>
        /// 16进制字符串
        /// </summary>
        Hex = 2
    }
    public static class EncryptionAes
    {
        #region AES加密

        /// <summary>
        /// AES加密
        /// </summary>
        /// <param name="source"></param>
        /// <param name="key">密钥</param>
        /// <param name="iv">初始向量</param>
        /// <param name="padding">填充模式</param>
        /// <param name="mode">加密模式</param>
        /// <param name="ciphertextType">密文类型</param>
        /// <returns></returns>
        public static (bool isSuccess, string text) AESEncrypt(this string source, string key, string iv = "", PaddingMode padding = PaddingMode.PKCS7, CipherMode mode = CipherMode.CBC, CiphertextType ciphertextType = CiphertextType.Base64)
        {
            try
            {
                byte[] keyBytes = Encoding.UTF8.GetBytes(key);
                byte[] textBytes = Encoding.UTF8.GetBytes(source);
                byte[] ivBytes = Encoding.UTF8.GetBytes(iv);

                byte[] useKeyBytes = new byte[16];
                byte[] useIvBytes = new byte[16];

                if (keyBytes.Length > useKeyBytes.Length)
                {
                    Array.Copy(keyBytes, useKeyBytes, useKeyBytes.Length);
                }
                else
                {
                    Array.Copy(keyBytes, useKeyBytes, keyBytes.Length);
                }
                if (ivBytes.Length > useIvBytes.Length)
                {
                    Array.Copy(ivBytes, useIvBytes, useIvBytes.Length);
                }
                else
                {
                    Array.Copy(ivBytes, useIvBytes, ivBytes.Length);
                }

                Aes aes = System.Security.Cryptography.Aes.Create();
                aes.KeySize = 256;//秘钥的大小,以位为单位,128,256等
                aes.BlockSize = 128;//支持的块大小
                aes.Padding = padding;//填充模式
                aes.Mode = mode;
                aes.Key = useKeyBytes;
                aes.IV = useIvBytes;//初始化向量,如果没有设置默认的16个0

                ICryptoTransform cryptoTransform = aes.CreateEncryptor();
                byte[] resultBytes = cryptoTransform.TransformFinalBlock(textBytes, 0, textBytes.Length);
                return (true, CipherByteArrayToString(resultBytes, ciphertextType));
            }
            catch (Exception ex)
            {
                return (false, ex.Message);
            }
        }

        #endregion



        #region AES解密

        /// <summary>
        /// AES解密
        /// </summary>
        /// <param name="source"></param>
        /// <param name="key">密钥</param>
        /// <param name="iv">初始向量</param>
        /// <param name="padding">填充模式</param>
        /// <param name="mode">加密模式</param>
        /// <param name="ciphertextType">密文类型</param>
        /// <returns></returns>
        public static (bool isSuccess, string text) AESDecrypt(this string source, string key, string iv = "", PaddingMode padding = PaddingMode.PKCS7, CipherMode mode = CipherMode.CBC, CiphertextType ciphertextType = CiphertextType.Base64)
        {
            try
            {
                byte[] keyBytes = Encoding.UTF8.GetBytes(key);
                byte[] textBytes = CiphertextStringToByteArray(source, ciphertextType);
                byte[] ivBytes = Encoding.UTF8.GetBytes(iv);

                byte[] useKeyBytes = new byte[16];
                byte[] useIvBytes = new byte[16];

                if (keyBytes.Length > useKeyBytes.Length)
                {
                    Array.Copy(keyBytes, useKeyBytes, useKeyBytes.Length);
                }
                else
                {
                    Array.Copy(keyBytes, useKeyBytes, keyBytes.Length);
                }

                if (ivBytes.Length > useIvBytes.Length)
                {
                    Array.Copy(ivBytes, useIvBytes, useIvBytes.Length);
                }
                else
                {
                    Array.Copy(ivBytes, useIvBytes, ivBytes.Length);
                }

                Aes aes = System.Security.Cryptography.Aes.Create();
                aes.KeySize = 256;//秘钥的大小,以位为单位,128,256等
                aes.BlockSize = 128;//支持的块大小
                aes.Padding = padding;//填充模式
                aes.Mode = mode;
                aes.Key = useKeyBytes;
                aes.IV = useIvBytes;//初始化向量,如果没有设置默认的16个0

                ICryptoTransform decryptoTransform = aes.CreateDecryptor();
                byte[] resultBytes = decryptoTransform.TransformFinalBlock(textBytes, 0, textBytes.Length);
                return (true, Encoding.UTF8.GetString(resultBytes));
            }
            catch (Exception ex)
            {
                return (false, ex.Message);
            }
        }

        /// <summary>
        /// 通过密文返回加密byte数组
        /// </summary>
        /// <param name="ciphertext"></param>
        /// <param name="ciphertextType"></param>
        /// <returns></returns>
        public static byte[] CiphertextStringToByteArray(string ciphertext, CiphertextType ciphertextType)
        {
            byte[] cipherByte;
            switch (ciphertextType)
            {
                case CiphertextType.Base64:
                    cipherByte = Convert.FromBase64String(ciphertext);
                    break;
                case CiphertextType.Hex:
                    cipherByte = HexStringToByteArray(ciphertext);
                    break;
                default:
                    cipherByte = Convert.FromBase64String(ciphertext);
                    break;
            }
            return cipherByte;
        }

        /// <summary>
        /// 通过加密的Byte数组返回加密后的密文
        /// </summary>
        /// <param name="cipherByte"></param>
        /// <param name="ciphertextType"></param>
        /// <returns></returns>
        public static string CipherByteArrayToString(byte[] cipherByte, CiphertextType ciphertextType)
        {
            string ciphertext = "";
            switch (ciphertextType)
            {
                case CiphertextType.Base64:
                    ciphertext = Convert.ToBase64String(cipherByte);
                    break;
                case CiphertextType.Hex:
                    ciphertext = ByteArrayToHexString(cipherByte);
                    break;
                default:
                    ciphertext = Convert.ToBase64String(cipherByte);
                    break;
            }
            return ciphertext;
        }

        /// <summary>
        /// Hex字符串转Byte
        /// </summary>
        /// <param name="hexContent"></param>
        /// <returns></returns>
        public static byte[] HexStringToByteArray(string hexContent)
        {
            hexContent = hexContent.Replace(" ", "");
            byte[] buffer = new byte[hexContent.Length / 2];
            for (int i = 0; i < hexContent.Length; i += 2)
            {
                buffer[i / 2] = (byte)Convert.ToByte(hexContent.Substring(i, 2), 16);
            }
            return buffer;
        }
        /// <summary>
        /// Byte转Hex字符串
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static string ByteArrayToHexString(byte[] data)
        {
            StringBuilder sb = new StringBuilder(data.Length * 3);
            foreach (byte b in data)
            {
                sb.Append(Convert.ToString(b, 16).PadLeft(2, '0'));
            }
            return sb.ToString().ToUpper();
        }
        #endregion
    }

调用AES加解密


    class Program
    {
        static void Main(string[] args)
        {
            var ddd = "111111111" .AESEncrypt("3EDsw9[A4W5FdS{]3W","0102030504030201", PaddingMode.PKCS7);
            Console.WriteLine($"加密:{ddd.text}");
            Console.WriteLine($"解密:{(ddd.text.AESDecrypt("3EDsw9[A4W5FdS{]3W", "0102030504030201", PaddingMode.PKCS7)).text}");
            Console.Read();
        }
    }
.Net Core实现AES加解密-第0张图片

示例下载

.Net Core实现AES加解密示例



转载请注明:清风亦平凡 » .Net Core实现AES加解密

]]>
https://www.mlplus.net/2020/09/02/net-core-encryption-aes/feed/ 0
asp .net core 不使用构造函数获得注入的对象 https://www.mlplus.net/2020/08/08/aspnetcorediget/ https://www.mlplus.net/2020/08/08/aspnetcorediget/#comments Sat, 08 Aug 2020 01:00:00 +0000 https://www.mlplus.net/?p=3026 使用asp .net core 2.1使用自带的依赖注入,自带的依赖注入是构造函数注入。有些情况,构造函数注入并不能满足需求,所以需要使用其他方法获取指定实例。

    
public interface IEngine
 {
        /// <summary>
        /// 类型构建
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        object Resolve(Type type);
}
    
public class GetEngine: IEngine
{
        private IServiceProvider _serviceProvider;
        public GetEngine(IServiceProvider serviceProvider)
        {
            this._serviceProvider = serviceProvider;
        }

        /// <summary>
        /// 类型构建
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public object Resolve(Type type)
        {
            return _serviceProvider.GetService(type);
        }
}

public class EnginContext
 {
        private static IEngine _engine;

        [MethodImpl(MethodImplOptions.Synchronized)]
        public static IEngine Initialize(IEngine engine)
        {
            if (_engine == null)
                _engine = engine;
            return _engine;
        }

        public static IEngine Current
        {
            get
            {
                return _engine;
            }
        }
 }

需要在 Startup.csConfigure方法中调用,如下:


EnginContext.Initialize(new GetEngine(app.ApplicationServices));

完整示例如下:


public void Configure(IApplicationBuilder app, IHostingEnvironment env)
 {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                app.UseHsts();
            }

            EnginContext.Initialize(new GetEngine(app.ApplicationServices));

            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
  }

新增一个类Information进行测试,如下:


    public class Information
    {
        private string Name => "Dot Net Core";
        public string GetName()
        {
            return $"你好!{Name}";
        }
    }

ConfigureServices方法中将新增的类进行注入。


        
        public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            services.AddTransient<Information>();
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

通过以下方式进行获取已经注入的对象。


EnginContext.Current.Resolve(typeof(Information));

使用示例如下:


        public IActionResult About()
        {
            ViewData["Message"] = "Your application description page.";
            var info= EnginContext.Current.Resolve(typeof(Information)) as Information;
            ViewData["Message"] = info.GetName();
            return View();
        }
asp .net core 不使用构造函数获得注入的对象-第0张图片

示例下载

示例



转载请注明:清风亦平凡 » asp .net core 不使用构造函数获得注入的对象

]]>
https://www.mlplus.net/2020/08/08/aspnetcorediget/feed/ 2
C#实现图片压缩 https://www.mlplus.net/2020/04/04/csharpimagecompress/ https://www.mlplus.net/2020/04/04/csharpimagecompress/#respond Fri, 03 Apr 2020 16:00:00 +0000 https://www.mlplus.net/?p=2666 有些时候会在博客中分享一些拍摄的照片,而这些拍摄照片的大小均在1.5M以上,随随便便分享点图片占用的存储空间就有点惊人。为了节省存储空间,避免尽早将剩余存储空间消耗完毕,所以就考虑压缩一下图片。这里来做下记录。


using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

namespace ImageCompress
{
    class Program
    {
        static void Main(string[] args)
        {
            string path = $"{Path.Combine(Directory.GetCurrentDirectory(), "image")}";
            CompressionImageProcess.CompressionImage($"{Path.Combine(path, "IMG_20200308_161707.jpg")}",$"{Path.Combine(path, "compression")}",50);
            Console.WriteLine("OK");
        }
    }
    public class CompressionImageProcess
    {
        public static void CompressionImage(string imagePath, string targetfolder, long quality = 100)
        {
            if (!File.Exists(imagePath))
            {
                throw new FileNotFoundException();
            }
            if (!Directory.Exists(targetfolder))
            {
                Directory.CreateDirectory(targetfolder);
            }
            FileInfo fileInfo = new FileInfo(imagePath);
            string fileName = fileInfo.Name.Replace(fileInfo.Extension, "");
            string fileFullName = Path.Combine($"{targetfolder}", $"{fileName}{fileInfo.Extension}");

            var iamgeByte = CompressionImage(imagePath, quality);
            MemoryStream ms = new MemoryStream(iamgeByte);
            Image image = System.Drawing.Image.FromStream(ms);
            image.Save(fileFullName);
            ms?.Close();
            ms?.Dispose();
            image?.Dispose();
        }
        private static byte[] CompressionImage(string imagePath, long quality)
        {
            using (FileStream fileStream = new FileStream(imagePath, FileMode.Open))
            {
                using (System.Drawing.Image img = System.Drawing.Image.FromStream(fileStream))
                {
                    using (Bitmap bitmap = new Bitmap(img))
                    {
                        ImageCodecInfo CodecInfo = GetEncoder(img.RawFormat);
                        System.Drawing.Imaging.Encoder myEncoder = System.Drawing.Imaging.Encoder.Quality;
                        EncoderParameters myEncoderParameters = new EncoderParameters(1);
                        EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, quality);
                        myEncoderParameters.Param[0] = myEncoderParameter;
                        using (MemoryStream ms = new MemoryStream())
                        {
                            bitmap.Save(ms, CodecInfo, myEncoderParameters);
                            myEncoderParameters?.Dispose();
                            myEncoderParameter?.Dispose();
                            return ms.ToArray();
                        }
                    }
                }
            }
        }

        private static ImageCodecInfo GetEncoder(ImageFormat format)
        {
            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
            foreach (ImageCodecInfo codec in codecs)
            {
                if (codec.FormatID == format.Guid)
                {
                    return codec;
                }
            }
            return null;
        }
    }
}


下图大小为1.5M,压缩质量设置了50,压缩后的大小为306.6k,压缩前后的质量清晰度还可以接受。

C#实现图片压缩-第0张图片
C#实现图片压缩-第1张图片

示例下载

图片压缩示例
其实示例代码写完就觉得自己有点傻,发现WordPress图片压缩插件也有不少,以后能省则省吧!



转载请注明:清风亦平凡 » C#实现图片压缩

]]>
https://www.mlplus.net/2020/04/04/csharpimagecompress/feed/ 0
C#实现大文本文件切割/分割 https://www.mlplus.net/2020/04/03/csharpcutbigfile/ https://www.mlplus.net/2020/04/03/csharpcutbigfile/#respond Fri, 03 Apr 2020 01:00:00 +0000 https://www.mlplus.net/?p=2646 因某些原因,需要打开日志进行排查,本来是很简单的事情,当看到日之后就觉得力不从心。日志文件太大了,将近5G,自己电脑打不开这种大文件。一开始就想到了文件分割,然后就再网上找一些分割工具,找了几个分割工具杀毒软件都报毒。找工具也真是个麻烦的事情,找到半天没有见到一个合适的。其中一个不报毒(主动查杀了一次)的,双击后也打不开,这个时候我就怀疑自己中招(中毒)了,到底有没有中招暂时不太清楚,所以赶紧利用杀毒软件查杀病毒。在查杀病毒的期间自己就动手使用C#写一个简单的文本文件分割工具。这里就记录一下.


using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;

namespace TxtFileCutApart
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length < 3)
            {
                Console.WriteLine("缺少参数。");
                return;
            }
            if (!File.Exists(args[0]))
            {
                Console.WriteLine("分割文件不存在");
                return;
            }
            if (!Directory.Exists(args[1]))
            {
                Console.WriteLine("分割目录不存在");
                return;
            }
            long size = Convert.ToInt64(args[2]);
            List types = new List() {"size","count","row" };
            Dictionary dicFileCut = new Dictionary() { {"size",new TxtFileCutApartFileSize() }, { "count", new TxtFileCutApartFileCount() } , { "row", new TxtFileCutApartRows() } };
            string type =types[0];
            if (args.Length == 4&&types.Contains(args[3]))
            {
                type =args[3];
            }
            Console.WriteLine("开始分割,请稍候……");
            Stopwatch st = new Stopwatch();
            TxtFileCutApart txtFileCut = dicFileCut[type];
            st.Start();
            txtFileCut.FileCut(args[0],args[1],size);
            st.Stop();
            Console.WriteLine($"完成,耗时:{st.ElapsedMilliseconds/1000}s");
        }
    }

    public abstract class TxtFileCutApart
    {
        public abstract void FileCut(string sourcePath, string targetFolder,long size);
    }
    public  class TxtFileCutApartFileCount : TxtFileCutApart
    {
        public override void FileCut(string sourcePath, string targetFolder, long fileCount)
        {
            if (fileCount <= 0)
            {
                return;
            }
            FileInfo fileInfo = new FileInfo(sourcePath);
            string fileName = fileInfo.Name.Replace(fileInfo.Extension, "");
            long fileLength = fileInfo.Length;
            FileStream fsRead = new FileStream(sourcePath, FileMode.Open, FileAccess.Read, FileShare.Read);
            BinaryReader br = new BinaryReader(fsRead);
            int defaultBurrerLength = 1024 * 1024;
            long fileSizeLength = Convert.ToInt64((Math.Ceiling(fileLength / Convert.ToDouble(fileCount))));
            byte[] buffer = new byte[defaultBurrerLength];
            int readLength = 0;
            int fileIndex = 1;
            long readFileLength = 0;
            while (readFileLength < fileLength)
            {
                string writeFile = Path.Combine(targetFolder, $"{fileName}_{fileIndex}{fileInfo.Extension}");
                FileStream fsWrite = new FileStream(writeFile, FileMode.CreateNew, FileAccess.Write);
                BinaryWriter bw = new BinaryWriter(fsWrite);
                long singleFileLength = 0;
                while ((readLength = br.Read(buffer, 0, buffer.Length)) > 0)
                {
                    bw.Write(buffer, 0, readLength);
                    readFileLength += readLength;
                    singleFileLength += readLength;
                    if (singleFileLength >= fileSizeLength)
                    {
                        bw?.Close();
                        bw?.Dispose();
                        fsWrite?.Close();
                        fsWrite?.Dispose();
                        break;
                    }
                }
                bw?.Close();
                bw?.Dispose();
                fsWrite?.Close();
                fsWrite?.Dispose();
                fileIndex++;
            }
            br?.Close();
            br?.Dispose();
            fsRead?.Close();
            fsRead.Dispose();
        }
    }
    public  class TxtFileCutApartFileSize : TxtFileCutApart
    {
        public override void FileCut(string sourcePath, string targetFolder, long fileSize)
        {
            if (fileSize <= 0)
            {
                return;
            }
            FileInfo fileInfo = new FileInfo(sourcePath);
            string fileName = fileInfo.Name.Replace(fileInfo.Extension, "");

            FileStream fsRead = new FileStream(sourcePath, FileMode.Open, FileAccess.Read, FileShare.Read);
            BinaryReader br = new BinaryReader(fsRead);
            int defaultBurrerLength = 1024 * 1024;
            long fileSizeLength = fileSize * defaultBurrerLength;
            byte[] buffer = new byte[defaultBurrerLength];
            int readLength = 0;
            int fileIndex = 1;
            long fileLength = fileInfo.Length;
            long readFileLength = 0;
            while (readFileLength < fileLength)
            {
                string writeFile = Path.Combine(targetFolder, $"{fileName}_{fileIndex}{fileInfo.Extension}");
                FileStream fsWrite = new FileStream(writeFile, FileMode.CreateNew, FileAccess.Write);
                BinaryWriter bw = new BinaryWriter(fsWrite);
                long singleFileLength = 0;
                while ((readLength = br.Read(buffer, 0, buffer.Length)) > 0)
                {
                    bw.Write(buffer, 0, readLength);
                    readFileLength += readLength;
                    singleFileLength += readLength;
                    if (singleFileLength >= fileSizeLength)
                    {
                        bw?.Close();
                        bw?.Dispose();
                        fsWrite?.Close();
                        fsWrite?.Dispose();
                        break;
                    }
                }
                bw?.Close();
                bw?.Dispose();
                fsWrite?.Close();
                fsWrite?.Dispose();
                fileIndex++;
            }
            br?.Close();
            br?.Dispose();
            fsRead?.Close();
            fsRead.Dispose();
        }
    }
    public  class TxtFileCutApartRows: TxtFileCutApart
    {
        public override void FileCut(string sourcePath, string targetFolder, long row)
        {
            if (row <= 0)
            {
                return;
            }
            FileInfo fileInfo = new FileInfo(sourcePath);
            string fileName = fileInfo.Name.Replace(fileInfo.Extension, "");
            string lineData = "";
            StreamReader sr = new StreamReader(sourcePath);
            StreamWriter sw = null;

            long readEowIndex = 1, index = 1;
            while ((lineData = sr.ReadLine()) != null)
            {
                if (sw == null || readEowIndex >= row)
                {
                    sw?.Close();
                    sw?.Dispose();
                    string _fileName = Path.Combine(targetFolder, $"{fileName}_{index}{fileInfo.Extension}");
                    sw = new StreamWriter(_fileName);
                    readEowIndex = 1;
                    index++;
                }
                if (row >= readEowIndex)
                {
                    sw.WriteLine(lineData);
                    sw.Flush();
                    readEowIndex++;
                }
            }
            sw?.Close();
            sw?.Dispose();
            sr?.Close();
            sr.Dispose();
        }
    }
}

小的切割工具可以按照大小(兆)、按照文件数量、按照行来进行分割。

dotnet TxtFileCutApart.dll 文件路径 分割后保存目录 数值 分割类型

具体使用方法,如下图所示:

C#实现大文本文件切割/分割-第0张图片
C#实现大文本文件切割/分割-第1张图片
C#实现大文本文件切割/分割-第2张图片
C#实现大文本文件切割/分割-第3张图片

源码下载

C#超大文本文件分割小工具



转载请注明:清风亦平凡 » C#实现大文本文件切割/分割

]]>
https://www.mlplus.net/2020/04/03/csharpcutbigfile/feed/ 0
C#随机打乱List数组中项的顺序 https://www.mlplus.net/2020/04/01/csharprandomlist/ https://www.mlplus.net/2020/04/01/csharprandomlist/#respond Tue, 31 Mar 2020 16:00:00 +0000 https://www.mlplus.net/?p=2640 C#随机打乱List数组中项的顺序-第0张图片

突然有一个需求,要求数组中数据在返回前进行随机打乱,于是就记录下来。

方法一


        public static void ListRandom<T>(List<T> sources)
        {
            Random rd = new Random();
            int index = 0;
            T temp;
            for (int i = 0; i < sources.Count; i++)
            {
                index = rd.Next(0, sources.Count - 1);
                if (index != i)
                {
                    temp = sources[i];
                    sources[i] = sources[index];
                    sources[index] = temp;
                }
            }
        }

方法二


        public static List<T> ListRandom<T>(List<T> sources)
        {
            var random = new Random();
            var resultList = new List<T>();
            foreach (var item in sources)
            {
                resultList.Insert(random.Next(resultList.Count), item);
            }
            return resultList;
        }

以上两个方法均可以简单的实现需求,但是方法一的性能要优于方法二。



转载请注明:清风亦平凡 » C#随机打乱List数组中项的顺序

]]>
https://www.mlplus.net/2020/04/01/csharprandomlist/feed/ 0
使用C#通过NTP同步本地Windows系统时间 https://www.mlplus.net/2020/02/08/usecsharpntpsyncwindowsdatetime/ https://www.mlplus.net/2020/02/08/usecsharpntpsyncwindowsdatetime/#comments Sat, 08 Feb 2020 05:50:10 +0000 https://www.mlplus.net/?p=2342 每年春节买票都成为相当重要的事情,快人一步基本能尽可能的抢占先机。使用抢票软件尽可能的减少手工操作,从而提高抢票的成功机率,由于时间误差也可会丧失先机,所以同步系统时间也是重要一步。为了可以自动的同步本地时间,所以就使用C#实现一个时间同步小工具。

只针对Windows系统的时间同步且使用抢票平台的不在此列。
使用C#通过NTP同步本地Windows系统时间-第0张图片

using System;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace WindowsDateTimeSynchronization
{

    public class DateTimeSynchronization
    {
        [StructLayout(LayoutKind.Sequential)]
        private struct Systemtime
        {
            public short year;
            public short month;
            public short dayOfWeek;
            public short day;
            public short hour;
            public short minute;
            public short second;
            public short milliseconds;
        }

        [DllImport("kernel32.dll")]
        private static extern bool SetLocalTime(ref Systemtime time);

        private static uint swapEndian(ulong x)
        {
            return (uint)(((x & 0x000000ff) << 24) +
            ((x & 0x0000ff00) << 8) +
            ((x & 0x00ff0000) >> 8) +
            ((x & 0xff000000) >> 24));
        }

        /// <summary>
        /// 设置系统时间
        /// </summary>
        /// <param name="dt">需要设置的时间</param>
        /// <returns>返回系统时间设置状态,true为成功,false为失败</returns>
        private static bool SetLocalDateTime(DateTime dt)
        {
            Systemtime st;
            st.year = (short)dt.Year;
            st.month = (short)dt.Month;
            st.dayOfWeek = (short)dt.DayOfWeek;
            st.day = (short)dt.Day;
            st.hour = (short)dt.Hour;
            st.minute = (short)dt.Minute;
            st.second = (short)dt.Second;
            st.milliseconds = (short)dt.Millisecond;
            bool rt = SetLocalTime(ref st);
            return rt;
        }
        private static IPAddress iPAddress = null;
        public static bool Synchronization(string host,out DateTime syncDateTime,out string message)
        {
            syncDateTime = DateTime.Now;
            try
            {
                message = "";
                if (iPAddress == null)
                {
                    var iphostinfo = Dns.GetHostEntry(host);
                    var ntpServer = iphostinfo.AddressList[0];
                    iPAddress = ntpServer;
                }
                //NTP消息大小摘要是16字节 (RFC 2030)
                byte[] ntpData = new byte[48];
                //设置跳跃指示器、版本号和模式值
                ntpData[0] = 0x1B; // LI = 0 (no warning), VN = 3 (IPv4 only), Mode = 3 (Client Mode)
                IPAddress ip = iPAddress;
                // NTP服务给UDP分配的端口号是123
                IPEndPoint ipEndPoint = new IPEndPoint(ip, 123);
                // 使用UTP进行通讯
                Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                DateTime dtStart = DateTime.Now;
                socket.Connect(ipEndPoint);
                socket.ReceiveTimeout = 3000;
                socket.Send(ntpData);
                socket.Receive(ntpData);
                socket?.Close();
                socket?.Dispose();
                DateTime dtEnd = DateTime.Now;
                //传输时间戳字段偏移量,以64位时间戳格式,应答离开客户端服务器的时间
                const byte serverReplyTime = 40;
                // 获得秒的部分
                ulong intPart = BitConverter.ToUInt32(ntpData, serverReplyTime);
                //获取秒的部分
                ulong fractPart = BitConverter.ToUInt32(ntpData, serverReplyTime + 4);
                //由big-endian 到 little-endian的转换
                intPart = swapEndian(intPart);
                fractPart = swapEndian(fractPart);
                ulong milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000UL);
                // UTC时间
                DateTime webTime = (new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)).AddMilliseconds(milliseconds);
                //webTime = webTime.Subtract(-(dtEnd - dtStart));
                //本地时间
                DateTime dt = webTime.ToLocalTime();
                bool isSuccess = SetLocalDateTime(dt);
                syncDateTime = dt;

            }
            catch (Exception ex)
            {
                message = ex.Message;
                return false;
            }
            return true;

        }
    }

    class Program
    {

        static void Main(string[] args)
        {
            Console.WriteLine("ntp时间同步");
            DateTime dt = DateTime.Now;
            Console.WriteLine($"现在本地时间:{dt.ToString("yyyy-MM-dd HH:mm:ss")}");
            string errorMessage = "";
            DateTimeSynchronization.Synchronization("ntp.aliyun.com",out dt,out errorMessage);
            if (string.IsNullOrWhiteSpace(errorMessage))
            {
                Console.WriteLine($"同步时间:{dt.ToString("yyyy-MM-dd HH:mm:ss")}");
            }
            Console.Read();
        }


    }
}


示例下载

通过NTP服务器同步Windows系统时间



转载请注明:清风亦平凡 » 使用C#通过NTP同步本地Windows系统时间

]]>
https://www.mlplus.net/2020/02/08/usecsharpntpsyncwindowsdatetime/feed/ 4
ASP.NET Core 配置跨域(CORS) https://www.mlplus.net/2020/01/13/aspnetcorecors/ https://www.mlplus.net/2020/01/13/aspnetcorecors/#respond Mon, 13 Jan 2020 01:00:00 +0000 https://www.mlplus.net/?p=2235 由于项目中需要实时消息,所以就使用了ASP.NET(Core) SignalR实时通讯库。因为业务服务与通讯服务是独立的,所以涉及到跨域的问题, 浏览器抛出的异常非常明显,这个是明显跨域相关内容。 报错信息如下:

ASP.NET Core 配置跨域(CORS)-第0张图片

Access to XMLHttpRequest at ‘http://192.168.2.13:5005/api/v1/commommessage/messageHub/negotiate’ from origin ‘http://127.0.0.1:5500’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

CORS(跨域资源共享)是一种W3C标准,允许服务器放宽同源策略。使用CORS,服务器可以在显式允许某些跨域请求时拒绝其他跨域请求。CORS是相比其他跨域技术(比如JSONP)更安全、更灵活。

ASP.NET Core跨域问题需要再 StartUp.cs 文件中进行相关配置。

ConfigureServices方法增加内容

        services.AddCors(options => options.AddPolicy("CorsPolicy",
            builder =>
            {
                builder.AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowAnyOrigin()
                    .AllowCredentials();
            }));

完整的ConfigureServices方法示例如下:


        public IServiceProvider ConfigureServices(IServiceCollection services)
        {

            services.AddCors(options => options.AddPolicy("CorsPolicy",
                builder =>
                {
                    builder.AllowAnyMethod()
                        .AllowAnyHeader()
                        .AllowAnyOrigin()
                        .AllowCredentials();
                }));

            services.AddMvc(options => { options.Filters.Add(); })
                .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
                //全局配置Json序列化处理
                .AddJsonOptions(options =>
                {
                    options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
                })
                .AddFluentValidation();

            services.AddSignalR();

            return services.AutofacServiceProvider();
        }

在Configure方法中添加

app.UseCors("CorsPolicy");

完整Configure方法示例:


        public void Configure(IApplicationBuilder app, IHostingEnvironment env,
                              ILoggerFactory loggerFactory,
                              IOptions config)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            if (env.IsProduction())
            {
                //注册服务到consul
                app.UseConsul();

                using (HttpClient client = new HttpClient())
                {
                    StringContent content = new StringContent(JsonConvert.SerializeObject(config.Value.RegisterParams));
                    content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
                    HttpResponseMessage message = client.PostAsync(config.Value.Server.Address, content).Result;
                };
            }
            app.UseCors("CorsPolicy");
            loggerFactory.AddNLog();
            env.ConfigureNLog("nlog.config");//读取Nlog配置文件
            app.UseSignalR(routes =>
            {
                routes.MapHub("/api/v1/examinations/messageHub");
            });
            app.UseMvc();
        }
特别注意:app.UseCors()必须放在app.UseMvc()之前。

本以为这就算完了,但是万万没想到啊,依然没有什么用。浏览器控制台错误依然存在,.net core也提示了相关警告信息,差点就忘了现在使用.net core 版本和以前不一样了。

The CORS protocol does not allow specifying a wildcard (any) origin and credentials at the same time. Configure the policy by listing individual origins if credentials needs to be supported.

ASP.NET Core 配置跨域(CORS)-第1张图片

目前使用的是ASP.NET CORE 2.1, 其Cors组件已经升级,出于安全考虑必须明确要允许的内容。 这是安全的一部分,不可以这样操作。 如果要允许凭据,则Access-Control-Allow-Origin不能使用*通配符。 必须指定确切的协议++端口。

这个问题其实也很好解决,只需要给予一个可信列表即可。修改内容如下:


        services.AddCors(options => options.AddPolicy("CorsPolicy",
            builder =>
            {
                builder.WithOrigins(new string[] { "http://127.0.0.1:5500" })
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowCredentials();
            }));

如果真的就不想做任何限制,其实也是有办法的。只需要将AllowAnyOrigin替换为SetIsOriginAllowed(_ => true)就可以解决。


       services.AddCors(options => options.AddPolicy("CorsPolicy",
            builder =>
            {
                builder.AllowAnyMethod()
                    .SetIsOriginAllowed(_ => true)
                    .AllowAnyHeader()
                    .AllowCredentials();
            }));

除了前面的两个方法以外,其实还可以自定义中间件。添加Cors处理类。如下:


    public class CorsMiddleware
    {
        private readonly RequestDelegate next;

        public CorsMiddleware(RequestDelegate next)
        {
            this.next = next;
        }
        public async Task Invoke(HttpContext context)
        {
            if (context.Request.Headers.ContainsKey(CorsConstants.Origin))
            {
                context.Response.Headers.Add("Access-Control-Allow-Origin", context.Request.Headers["Origin"]);
                context.Response.Headers.Add("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS,HEAD,PATCH");
                context.Response.Headers.Add("Access-Control-Allow-Headers", context.Request.Headers["Access-Control-Request-Headers"]);
                context.Response.Headers.Add("Access-Control-Allow-Credentials", "true");

                if (context.Request.Method.Equals("OPTIONS"))
                {
                    context.Response.StatusCode = StatusCodes.Status200OK;
                    return;
                }
            }

            await next(context);
        }
    }

Configure方法中添加如下内容即可。

app.UseMiddleware<CorsMiddleware>();



转载请注明:清风亦平凡 » ASP.NET Core 配置跨域(CORS)

]]>
https://www.mlplus.net/2020/01/13/aspnetcorecors/feed/ 0
Newtonsoft.Json序列化对象时循环引用异常处理 https://www.mlplus.net/2020/01/11/newtonsoftjsonreferenceloophandling/ https://www.mlplus.net/2020/01/11/newtonsoftjsonreferenceloophandling/#respond Sat, 11 Jan 2020 04:18:19 +0000 https://www.mlplus.net/?p=2256 因某些需求,需要将指定的对象序列化放到缓存中,在使用Newtonsoft.Json序列化时候报错,异常信息清晰明显就是循环引用问题。具体错误如下:

Newtonsoft.Json序列化对象时循环引用异常处理-第0张图片

详细异常如下:

fail: System.Exception[-2146233088]
Self referencing loop detected for property ‘Ancestor’ with type ”. Path ‘Descendants[0]’. Newtonsoft.Json.JsonSerializationException: Self referencing loop detected for property ‘Ancestor’ with type ‘*’. Path ‘Descendants[0]’.
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CheckForCircularReference(JsonWriter writer, Object value, JsonProperty property, JsonContract contract, JsonContainerContract containerContract, JsonProperty containerProperty)
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CalculatePropertyValues(JsonWriter writer, Object value, JsonContainerContract contract, JsonProperty member, JsonProperty property, JsonContract& memberContract, Object& memberValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType)
at Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType)
at Newtonsoft.Json.JsonConvert.SerializeObjectInternal(Object value, Type type, JsonSerializer jsonSerializer)
at System.Threading.Tasks.ValueTask`1.get_Result()
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeActionMethodAsync()
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeNextActionFilterAsync()
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextExceptionFilterAsync()

解决方法

在序列化或者反序列化指定JsonSerializerSettings即可解决:

  var setting = new JsonSerializerSettings();
  setting.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
  var json = JsonConvert.SerializeObject(data, setting)

如果想要循环引用的数据得以保留,以便后面反序列化时能还原数据,所以将循环引用设置为序列化 ,如下方式:

var setting = new JsonSerializerSettings();
setting.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
setting.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
var json = JsonConvert.SerializeObject(data, setting);



转载请注明:清风亦平凡 » Newtonsoft.Json序列化对象时循环引用异常处理

]]>
https://www.mlplus.net/2020/01/11/newtonsoftjsonreferenceloophandling/feed/ 0