清风博客 https://www.skyfinder.cc 关注IT世界,记录平凡生活 Tue, 16 May 2023 04:57:05 +0000 zh-CN hourly 1 https://wordpress.org/?v=6.2 Syncfusion.DocIO.Net.Core操作Word的第三方组件无限制版 https://www.skyfinder.cc/2023/05/16/syncfusion-docio-net-core/ https://www.skyfinder.cc/2023/05/16/syncfusion-docio-net-core/#respond Tue, 16 May 2023 04:54:47 +0000 https://www.skyfinder.cc/?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.Core操作Word的第三方组件无限制版

]]>
https://www.skyfinder.cc/2023/05/16/syncfusion-docio-net-core/feed/ 0
Guetzli高质量压缩图片算法工具 https://www.skyfinder.cc/2023/04/06/guetzli-jpeg-encoder/ https://www.skyfinder.cc/2023/04/06/guetzli-jpeg-encoder/#respond Thu, 06 Apr 2023 04:59:57 +0000 https://www.skyfinder.cc/?p=4181 介绍

Guetzli是一款JPEG编码器,其目标是在高视觉质量下实现卓越的压缩密度。Guetzli生成的图像通常比libjpeg生成的同等质量的图像小20-30%Guetzli只生成顺序的(非渐进的)JPEG,因为它们提供了更快的解压缩速度。

JPEG 图像的视觉质量与它的多阶段压缩过程有关:色彩空间变换,离散余弦变换,以及量化等等。Guetzli 具体针对量化阶段,图像视觉质量损失越多,输出图像尺寸越小。Guetzli 努力通过一个搜索算法,来克服 JPEG 格式的精神视觉模型与 Guetzli 的精神视觉模型之间的差别,以一种更全面更详细的方式来结合色彩感知和视觉掩蔽,从而在最小化损失和最小化图像尺寸中达到平衡。不过,尽管 Guetzli 可以使图像尺寸更小,但创建压缩图像所花费的时间要与目前的方法更长。

项目地址

guetzli

使用

Guetzli使用大量内存。您应该为每1MPix的输入图像提供300MB的内存。
Guetzli占用了大量的CPU时间。您应该指望每1MMix的输入图像使用大约1分钟的CPU。
Guetzli假设输入为sRGB配置文件,gamma为2.2。Guetzli将忽略图像中的任何颜色配置文件元数据。

要试用Guetzli,您需要构建下载Guetzli二进制文件。二进制文件读取PNGJPEG图像并创建优化的JPEG图像。

guetzli的使用比较简单,参数很少。默认采用95的质量,也可以通过 -quality 来指定大于等于84的质量,如果要小于84,需要修改源码。输入必须是符合转换要求的图片,输出则是jpeg格式图片。

什么样的图片才是符合要求的呢?

官方说明:

Only YUV color space input jpeg is supported

使用透明通道的png图、非YUVjpeg比如黑白图、gif图都是不能转换的。


guetzli [--quality Q] [--verbose] original.png output.jpg
guetzli [--quality Q] [--verbose] original.jpg output.jpg

guetzli 使用方式

压缩比例还是不错的,图像质量越高,体积越大压缩的比例就越高。目前测试7M的图片最大压缩到941 KB

请注意,Guetzli是为处理高质量图像而设计的。您应该始终倾向于提供未压缩的输入图像(例如,尚未使用任何JPEG编码器压缩的图像,包括Guetzli)。虽然它也适用于其他图像,但效果会较差。您可以尝试压缩一个封闭的样本高质量图像

您可以传递--quality Q参数来设置质量,单位相当于libjpeg质量。您还可以传递一个--verbose标志来查看所进行的编码尝试的跟踪。

请注意,JPEG图像不支持alpha通道(透明度)。如果输入是带有alpha通道的PNG,则在编码之前,它将覆盖在黑色背景上。

guetzli优劣

优势

兼容性比较好,输出的jpeg格式图片通用性非常高。没有webpsharpp那种协议不兼容的困扰。

压缩比率高,压缩质量比较好。

劣势

处理类型局限,并不能应对全部类型的图片。只能处理YUV颜色编码的图片。

时效性较差,图片越大处理越慢。7M的图片要1个多小时,不适合超高质量大体积的图片批量处理。

结论

guetzli是一款不错的JPEG编码器,在何种方面使用还需要根据自身的需求来确定。



转载请注明:清风博客 » Guetzli高质量压缩图片算法工具

]]>
https://www.skyfinder.cc/2023/04/06/guetzli-jpeg-encoder/feed/ 0
安全架构设计基本原则 https://www.skyfinder.cc/2023/03/29/%e5%ae%89%e5%85%a8%e6%9e%b6%e6%9e%84%e8%ae%be%e8%ae%a1%e5%9f%ba%e6%9c%ac%e5%8e%9f%e5%88%99/ https://www.skyfinder.cc/2023/03/29/%e5%ae%89%e5%85%a8%e6%9e%b6%e6%9e%84%e8%ae%be%e8%ae%a1%e5%9f%ba%e6%9c%ac%e5%8e%9f%e5%88%99/#respond Wed, 29 Mar 2023 04:51:35 +0000 https://www.skyfinder.cc/?p=4176 安全原则

在应用系统软件开发设计的过程中,对应用系统的总体设计应当满足如下安全原则

原则说明
最小权限原则
Least Privilege
应用软件的每个模块如进程、用户只能访问当下所必需的信息或者资源。赋予每一个合法动作最小的权限,以保护数据以及功能避免受到错误或者恶意行为的破坏。
权限分离原则
Separation of Duties
对业务的操作、管理和审计权限应该由软件中的不同角色的用户分别承担;普通用户和管理员用户信息应该存放在不同的数据表中。
深度防御原则
Defense in Depth
在应用程序对业务数据进行处理的每个阶段都要考虑安全性问题,不能仅在某个阶段做安全防御,这样单点防御一旦被突破将造成安全风险。
容错保护原则
Fail Secure
当程序出现故障时或系统异常当系统失败时,可以进入到一个失败保护的状态。如果用户请求失败,系统仍可保障安全
单点异常终止原则
Single Point of Failure
当用户提交数据超出预期时,应立即终止程序的执行,不要试图加以修正并继续执行下去。
外来代码安全原则
Least Third Party Components
严格控制第三方函数与插件的使用,对外来代码必须进行详细的安全测试。
代码重用原则
Leveraging Existing Components
尽可能的重用软件已有的模块,这样可以降低引入新的漏洞和攻击界面的可能性。
数据保护原则
Data Protection
对用户数据的保护功能应涵盖用户数据存储的完整性、用户数据传输保密性、数据传输的访问控制、剩余信息的保护、数据反转操作等内容;应对系统中关键数据(如用户密码等)的存储和网络传输时应采用加密保护,实用加密加密算法应该符合国际标准、国家标准和业界标准。
可审计原则
Auditing
在应用系统中设计审计日志记录的功能,并对应用系统产生的日志增加完备的审计功能。
开放设计原则
Open Design
开放设计与“不开放即安全”的原则相对而言,认为设计本身不应具有神秘感。这一原则的具体表现可以参见应用于加密设计的Kerchoff定律,“系统不应单纯依赖私密性,若落入敌人手中则毫无优势可言”;开放设计以提高系统兼容性和可扩展性。
抗抵赖原则
Anti Repudiation
对于涉及支付交易等重要的业务场景,系统设计应有效地防止通信双方抵赖,如采用电子证书签名等方式。
规范性
Standardization
系统设计所采用的安全技术和安全产品应符合国际标准、国家标准和业界标准,为系统的扩展升级、与其他系统的互联提供良好的基础。
可扩展性
Scalability
以当前业务安全需求为基础,充分考虑发展的需要,安全功能模块子系统以插件或接口方式以方便未来的扩展。
实用性
Practicable
安全功能设计需要尽可能的考虑投入产出比,同时尽量控制对用户体验的影响。
符合性
Regulatory Compliance
安全功能的设计尽可能的要符合国家规范、行业规范以及业界的通用标准,如等级保护等规范。



转载请注明:清风博客 » 安全架构设计基本原则

]]>
https://www.skyfinder.cc/2023/03/29/%e5%ae%89%e5%85%a8%e6%9e%b6%e6%9e%84%e8%ae%be%e8%ae%a1%e5%9f%ba%e6%9c%ac%e5%8e%9f%e5%88%99/feed/ 0
使用Fiddler进行移动端(手机)抓包 https://www.skyfinder.cc/2023/03/14/fiddler-mobile-setting/ https://www.skyfinder.cc/2023/03/14/fiddler-mobile-setting/#respond Tue, 14 Mar 2023 09:15:33 +0000 https://www.skyfinder.cc/?p=4156 背景

客户一个定制企业微信相关内容,其中自动登录成功某些功能不正常,手动登录的又未复现问题,程序都是相同的且问题确实存在。准备使用抓包看一下企业微信自动登录与非自动登录的行为区别,抓包工具选用了大名鼎鼎的Fiddler

Fiddler

Fiddler是一个http协议调试代理工具,它能够记录并检查所有你的电脑和互联网之间的http通讯,设置断点,查看所有的“进出”Fiddler的数据(指cookie,html,js,css等文件)。 Fiddler 要比其他的网络调试器要更加简单,因为它不仅仅暴露http通讯还提供了一个用户友好的格式。

Fiddler 是用C#写出来的,它包含一个简单却功能强大的基于JScript.NET 事件脚本子系统,它的灵活性非常棒,可以支持众多的http调试任务,并且能够使用.net框架语言进行扩展。

操作设置

设置允许远程电脑链接

依次选择Tools=>Options=>Connections=>勾选Allow remote computers to connect=>再点击OK

Fiddler tools
Fiddler connections
Fiddler的监听端口是8888,在远端设备上需要Fiddler所在设备的IP以及端口

查看本机IP

使用快捷键Windows+R调出运行小窗口,输入cmd回车即可。

运行
命令行显示本机IP

设置网络代理

打开移动设备Wlan,修改当前连接的网络。

移动设备WLAN

在以下页面进行代理设置,选择手动模式

移动设备WLAN代理设置页面

输入Fiddler所在设备的IP与端口后保存。

移动设备WLAN代理设置

手机打开浏览器进行测试,随便打开页面进行测试即可。

手机浏览移动页面

Fiddler可以看到在手机浏览的网页表示设置成功。

Fiddler查看抓取



转载请注明:清风博客 » 使用Fiddler进行移动端(手机)抓包

]]>
https://www.skyfinder.cc/2023/03/14/fiddler-mobile-setting/feed/ 0
安全风险状况说明 https://www.skyfinder.cc/2023/02/24/%e5%ae%89%e5%85%a8%e9%a3%8e%e9%99%a9%e7%8a%b6%e5%86%b5%e8%af%b4%e6%98%8e/ https://www.skyfinder.cc/2023/02/24/%e5%ae%89%e5%85%a8%e9%a3%8e%e9%99%a9%e7%8a%b6%e5%86%b5%e8%af%b4%e6%98%8e/#respond Fri, 24 Feb 2023 10:02:25 +0000 https://www.skyfinder.cc/?p=4138 安全风险状况等级说明
良好状态   信息系统处于良好运行状态,没有发现或只存在零星的低风险安全问题,此时只要保持现有安全策略就满足了本系统的安全等级要求。
预警状态信息系统中存在一些漏洞或安全隐患,此时需根据评估中发现的网络、主机、应用和管理等方面的问题对进行有针对性的加固或改进。
严重状态信息系统中发现存在严重漏洞或可能严重威胁到系统正常运行的安全问题,此时需要立刻采取措施,例如安装补丁或重新部署安全系统进行防护等等。
紧急状态信息系统面临严峻的网络安全态势,对组织的重大经济利益或政治利益可能造成严重损害。此时需要与其他安全部门通力协作采取紧急防御措施。

漏洞等级状况说明

低危漏洞 对系统造成较小的影响,攻击成本高,攻击场景较为苛刻,不会直接影响到系统的正常运行,攻击者可能无法通过该漏洞获得权限。
中危漏洞对系统造成一般的影响,攻击成本一般,在特定场景下将可影响系统的正常运行,攻击者需要配合其他安全漏洞方可间接获得权限。
高危漏洞对系统造成严重的影响,攻击成本低,一般情况下可直接利用而无需特定场景和要求,攻击者可直接利用该漏洞获得权限。



转载请注明:清风博客 » 安全风险状况说明

]]>
https://www.skyfinder.cc/2023/02/24/%e5%ae%89%e5%85%a8%e9%a3%8e%e9%99%a9%e7%8a%b6%e5%86%b5%e8%af%b4%e6%98%8e/feed/ 0
Docker构建VUE项目NPM构建134异常 https://www.skyfinder.cc/2023/02/23/docker-build-npm134-error/ https://www.skyfinder.cc/2023/02/23/docker-build-npm134-error/#respond Thu, 23 Feb 2023 08:10:53 +0000 https://www.skyfinder.cc/?p=4131 背景

一个前端的VUE项目,需要打一个Docker的镜像离线包。通过命令行构建镜像,通过等待比较长的时间后,NPM构建抛出异常。

异常内容

以下是关于这次异常的详细信息:

docker构建VUE项目异常图片
=> ERROR [build-stage 10/10] RUN npm run build                                                                 1222.9s
------
 > [build-stage 10/10] RUN npm run build:
#18 7.649
#18 7.649 > vue-antd-admin@0.7.4 build /app
#18 7.649 > vue-cli-service build
#18 7.649
#18 24.59
#18 24.60 -  Building for production...
#18 1221.3
#18 1221.3 <--- Last few GCs --->
#18 1221.3
#18 1221.3 [24:0x40d39c0]  1206698 ms: Mark-sweep (reduce) 986.8 (995.9) -> 986.1 (997.4) MB, 6393.8 / 0.1 ms  (average mu = 0.076, current mu = 0.019) allocation failure scavenge might not succeed
#18 1221.3
#18 1221.3
#18 1221.3 <--- JS stacktrace --->
#18 1221.3
#18 1221.3 FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
#18 1221.5  1: 0xa18da8 node::Abort() [/usr/local/bin/node]
#18 1221.5  2: 0x969880 node::FatalError(char const*, char const*) [/usr/local/bin/node]
#18 1221.5  3: 0xb74138 v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [/usr/local/bin/node]
#18 1221.5  4: 0xb742c4 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/usr/local/bin/node]
#18 1221.5  5: 0xd1c820 v8::internal::Heap::EnsureFromSpaceIsCommitted() [/usr/local/bin/node]
#18 1221.5  6: 0xd1d288  [/usr/local/bin/node]
#18 1221.5  7: 0xd2af4c v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/usr/local/bin/node]
#18 1221.5  8: 0xd2e41c v8::internal::Heap::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/usr/local/bin/node]
#18 1221.5  9: 0xd00714 v8::internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationType, v8::internal::AllocationOrigin) [/usr/local/bin/node]
#18 1221.5 10: 0x101ba54 v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [/usr/local/bin/node]
#18 1221.5 11: 0x137bb0c  [/usr/local/bin/node]
#18 1221.5 qemu: uncaught target signal 6 (Aborted) - core dumped
#18 1221.7 Aborted
#18 1221.8 npm ERR! code ELIFECYCLE
#18 1221.8 npm ERR! errno 134
#18 1221.8 npm ERR! vue-antd-admin@0.7.4 build: `vue-cli-service build`
#18 1221.8 npm ERR! Exit status 134
#18 1221.8 npm ERR!
#18 1221.8 npm ERR! Failed at the vue-antd-admin@0.7.4 build script.
#18 1221.8 npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
#18 1222.0
#18 1222.0 npm ERR! A complete log of this run can be found in:
#18 1222.0 npm ERR!     /root/.npm/_logs/2023-02-16T06_26_23_271Z-debug.log
------
executor failed running [/bin/sh -c npm run build]: exit code: 134


经过查看发现NPM异常退出代码134,通过错误代码了解到应该是内存不足导致的。优先确认自己使用的这台物理机器内存,并未发现问题。应该是Docker本身子系统内存问题,经过查看确认允许内存为2G,这内存允许范围的确可能出现内存不足的问题。将内存设置为4GB后保存,再次构建项目,经过一段时间的等待,Docker镜像构建成功。

Docker构建VUE项目NPM构建134异常-第1张图片

Docker构建VUE项目NPM构建134异常-第2张图片



转载请注明:清风博客 » Docker构建VUE项目NPM构建134异常

]]>
https://www.skyfinder.cc/2023/02/23/docker-build-npm134-error/feed/ 0
2023过年 https://www.skyfinder.cc/2023/01/24/2023%e8%bf%87%e5%b9%b4/ https://www.skyfinder.cc/2023/01/24/2023%e8%bf%87%e5%b9%b4/#respond Tue, 24 Jan 2023 13:33:41 +0000 https://www.skyfinder.cc/?p=4126 时间过得很快,一转眼就到了新的一年。今年公司春节假期放假比往年早很多,2023年1月16号就开始放假了,不晓得是因为疫情原因还是公司提供的福利,不管怎么说感觉很不错的。因为春运的火车票不太好买,所以就多计划了几天,在2013年1月11号就请假回家了。

回到家的第二天就到了医院进行一次检查,针对肺部的检查。由于新冠感染,导致除发烧以外的其他状没有好转,其实也不是没有一点好转,只是一直还是有症状,二十多天咳嗽依然存在。检查结果肺部有两个微小肺结节并且已经钙化,其实我是很担心的 。在新冠之前的一些检查中是没有见过的,我想还是这次新冠感染引起的。医生并没有说太多,只是说还有点炎症,开了一些止咳、镇咳、化痰的药物,并没有关于肺结节相关的药物。经过了解,肺结节是无法逆转的。

在回到老家后2天就先拜访几个长辈,这算是家族中一个规矩吧。在拜访他们之前,我还是确认了自己新冠转阴,否则我还真的担心会带走他们。

来了一些伙伴,重新约定了还款时间。重新约定时间就是为了防止过年去要账吧!其实挺烦人的,2019年借出去的钱,说好的2019年元宵节还给我,现在都2023年了。如果这次约定时间再不还给我,真的要考虑是不是要给他一个传票。这人我以后不打算在与其有其他的金钱往来了!

过年其实我觉得就是一个花钱多节日,在一年当中的春节还是拉动了消费的。每年都要有各种年货的购置,今年也是如此,其实我这个人挺不喜欢逛街的。其他的基本也是准备一些零散而又多的小年货,其实我在这种活动当中没有出什么力。

除夕夜其实也感觉没有什么意思,吃完饭也没有什么节目。至于春晚已经不看好多年了。除夕夜很早的就休息了,长大以后感觉过年好像已经没有了味道,尤其是最近几年就更加没有意思。也许是自己长大了,也许是少了一些人,又或者还是穷。每年农历一月初一都有早起的习俗,吃完饭后就开始到处拜年,去看看各个长辈,去看看那些年纪比较大的老人。初一这一天是一年当中最闲的一天,只有这一天是不允许干活的,可以肆无忌惮的休息。初二这天开始动工了,可以走亲戚或者进行其他工作。也许每年的初二在我们这里才是一个新的开始。

过年其实还是没有什么意思的。嗯!



转载请注明:清风博客 » 2023过年

]]>
https://www.skyfinder.cc/2023/01/24/2023%e8%bf%87%e5%b9%b4/feed/ 0
疑似新冠感染 https://www.skyfinder.cc/2022/12/25/%e7%96%91%e4%bc%bc%e6%96%b0%e5%86%a0%e6%84%9f%e6%9f%93/ https://www.skyfinder.cc/2022/12/25/%e7%96%91%e4%bc%bc%e6%96%b0%e5%86%a0%e6%84%9f%e6%9f%93/#respond Sat, 24 Dec 2022 18:31:58 +0000 https://www.skyfinder.cc/?p=4052 抗击新冠

独白

如标题所示,本博疑似新冠感染,为何说是疑似呢?因为并没有针对性的检查,没有做核酸,也没有做抗原,所以说是疑似。虽然说是疑似,经过几天的煎熬,这大概也就确认了。

2022-12-20直到晚上回来都没有什么不良症状,看了一会电视剧就躺下休息一会,哪知道就这样睡了。其实睡觉状态其实不太好,感觉又冷又热,但是自己还是没有立刻醒来。直到早上闹钟响起来才起床,感觉有点发热,头有点疼,眼睛也有点不舒服。当时自己猜测可能是发烧了,体温计之前没有买到,所以体温也没有办法的知。想了一下,还是请假吧!万一是新冠岂不是把其他同事给害了?按道理来讲,2022-12-21应该算是第一天,就当作第一天吧!

2022-12-21

今天感觉到好像发热,头有点疼,眼睛有点不舒服。为了避免其他同事被我这疑似新冠感染,就随即申请了居家办公。

为了更好的观测体温,所以还是准备买一直体温计。问了两家药店,一家没有体温计,一家说中午到货。在药店往返四次最终还是买到了一支普通的水银体温计,16元一支,以前同款仅仅2块钱,其中差额可想而知。这个时候已经是中午12点多了!

与预想的一致,的确是发烧了37.8,感觉不是很严重就想着挺一下。时间感觉过的很慢,但似乎又过的很快。开始浑身无力,慢慢的头疼加重腰疼腿疼蛋疼。是的,明显感觉到蛋疼,也没有听谁说过这件事情,是羞于言传吗?15点又量一次体温,体温已经高到38.9了,感觉顶不住了,所以就服用了备用药物《小儿氨酚黄那敏颗粒》进行退烧。这款药物还是不错的,体温很快就降到了38上下浮动。本来是要居家办公的,现在状态也没有办法工作了,无奈的就躺下啦!这样也许会舒服一点。剩下的就是喝水、躺!!!

症状

  • 头疼
  • 腰疼
  • 蛋疼
  • 腿疼
  • 高烧
  • 浑身无力
  • 眼睛不舒服

2022-12-22

今天依旧持续昨天的症状,唯一区别就是各种症状加重。一觉醒来体温干到40+,卧槽!看着体温计这个刻度,我竟然感觉不到自己发烧,感觉这个世界都是不真实的。为了制止这种状态持续发展就再次上退烧药。每次服用药物都没有让人失望,体温慢慢退回38度上下。一整天体温基本维持在38度上下浮动,为了更好的退烧,在安全范围内再次上药。今天居家办公写一点东西,但是也没有坚持太久。剩下的时间也是基本喝水、躺。晚上在彻底入睡之前体温降到了37.7上下。

症状

  • 头疼+
  • 腰疼+
  • 蛋疼+
  • 腿疼+
  • 高烧+
  • 浑身无力+
  • 眼睛不舒服+

2022-12-23

今天发烧算是退下来了,体温在37.5上下。这个体温算是一个好的开始吧!各种疼痛开始减轻。今天居家办公,居家办公一整天。期间在也没有服用任何药物,也只是喝水、吃了些水果。

症状

  • 头疼-
  • 腰疼-
  • 蛋疼-
  • 腿疼-
  • 浑身无力-
  • 眼睛不舒服-

2022-12-24

今天体温已经正常,体温在36.2-36.7之前间浮动。腰疼、蛋疼、腿疼、浑身无力、眼睛不舒服这些症状消失。今天新出现症状咳嗽,咳嗽间断性的咳嗽,同时有一种情况就是但凡说话必然咳嗽一阵,无比难受。今天一整天言语不多,也不想说话。头疼位置是后脑偏右,阵痛以及咳嗽疼痛。体温一直在正常范围内。喝水、吃水果、喝点止咳口服液。

症状

  • 头疼
  • 间断性咳嗽
  • 说话即咳嗽
  • 嗓子不舒服

2022-12-25

今天咳嗽依然持续且加重,嗓子依然不舒服,新增症状鼻塞、流鼻涕、流眼泪、打喷嚏。原来说话即咳嗽、头疼的症状消失。为了缓解鼻塞、流鼻涕的症状,使用了999感冒灵颗粒,不晓得有没有什么效果。试试看吧!卧槽!不晓得还得坚持几天,据说新冠已经可以确认的有一种后遗症,蛮可怕的。

症状

  • 咳嗽+
  • 嗓子不舒服
  • 鼻塞
  • 流鼻涕
  • 打喷嚏
  • 流眼泪

2022-12-26

今天的症状与昨天症状一致,没有什么增加或者减少。貌似严重程度发生少许变化,个人也不晓得这种变化是不是好的。鼻塞、流鼻涕、打喷嚏、流眼泪加重,咳嗽有少许减轻。昨天、今天也冲了感冒灵颗粒,貌似没有看到任何的效果。真的很希望这些症状赶紧消失,尽快恢复健康。

症状

  • 咳嗽-
  • 嗓子不舒服
  • 鼻塞+
  • 流鼻涕+
  • 打喷嚏
  • 流眼泪

2022-12-27

今天打喷嚏、流眼泪的情况消失了。但是咳嗽、嗓子不舒服、鼻塞流鼻涕依然存在,不过症状相比昨天有所减轻。目前身体状况来说可能还得几天,我的身体已经虚成这样了吗?

症状

  • 咳嗽
  • 嗓子不舒服
  • 鼻塞
  • 流鼻涕

2022-12-28

今天感觉症状没有增加也没有减少。咳嗽还是那么严重,鼻塞、流鼻涕、嗓子依然不舒服。这都8天了,为何还这样?感觉有必要去医院做一下CT检查,看看咳嗽是不是肺炎的问题。虽然其他症状没有增加,但是鼻炎好像是犯了,不晓得这算不算新增症状。还是一如既往的痛苦。什么时候才是一个终结?

症状

  • 咳嗽
  • 嗓子不舒服
  • 鼻塞
  • 流鼻涕

2022-12-29

今天好像也没有往好的方向改变,咳嗽、鼻塞、流鼻涕、嗓子不舒服一个症状都没有消失。真难搞呀!不晓得哪个人说的就是小感冒,这哪里是小感冒呀?

症状

  • 咳嗽
  • 嗓子不舒服
  • 鼻塞
  • 流鼻涕

2022-12-30

其实我内心是非常不希望这篇博客还有续写,特别是按照天的续写,我更希望这篇博客尽快写总结。但是这总是不能让人如愿,尽管我真的如此厌烦,还是要记录一下。今天症状并没有减轻,依旧是咳嗽比较突出,鼻塞、流鼻涕、嗓子不舒服。这个咳嗽我真的觉得应该去进一步检查一下,是传闻中的《白肺》,还是其他情况。打算就在这两天了!!!!

症状

  • 咳嗽
  • 嗓子不舒服
  • 鼻塞
  • 流鼻涕

2022-12-31

今天竟然也不是总结,其实我也是想的到的,只不过想多一点预期。很多事情往往都带有遗憾,这件事情作为一个普通人,我其实是很无力的 。我很想画一个句号,无奈现实给的是逗号。咳嗽症状依然没有什么好转,鼻塞、流鼻滴、嗓子不舒服症状都也没有什么改善。因为鼻塞、流鼻涕问题,我的鼻炎貌似也复发了。鼻炎相关的并发症好像也出现了,右耳已经开始感觉到闷了!这些能算新冠的症状吗?

新闻上讲很多有基础病或者其他疾病的人没有能顶得住,我突然明白为什么啦。身体不好的人很难顶得住的,因为并发症太麻烦了,搞不好就走了!今天我的话有点多了,希望自己可以顶得住吧!没有想到这个事情要跨年了!!!!!

症状

  • 咳嗽
  • 嗓子不舒服
  • 鼻塞
  • 流鼻涕

2023-01-01

真的没有出意外,这个事情跨年了,状态真的不是太好。今天症状算没有增加吧,与昨天的症状差不太多,也有一些差别。咳嗽这个症状太持久了,都十多天啦!要是能在床上这么持久,会不会笑疯了!要不就不咳嗽,要不就是咳嗽起来要命的咳。鼻塞、流鼻涕还是停不下来,这几个症状与嗓子不舒服比起来,嗓子的问题到是没有太大的事情。卧槽!!!

症状

  • 咳嗽
  • 嗓子不舒服
  • 鼻塞
  • 流鼻涕

2023-01-02

今天症状还是没有消退,不过咳嗽症状有所减轻,也只是减轻一点点,算是一个好的开始吧!希望真是一个好的开始!鼻塞、流鼻涕、嗓子不舒服依然存在。今天针对这个事情不太想多说,心太累了!

症状

  • 咳嗽
  • 嗓子不舒服
  • 鼻塞
  • 流鼻涕

2023-01-03

昨天刚说咳嗽有点减轻,今天咳嗽又加重,真是奇怪了,真烦呀!鼻塞、鼻涕、嗓子不舒服这些症状也没有什么好转。真的好暴躁呀!

症状

  • 咳嗽
  • 嗓子不舒服
  • 鼻塞
  • 流鼻涕

2023-01-04

今天咳嗽没有什么减轻,还是老样子。咳嗽、鼻塞、鼻涕、嗓子不舒服,目前三个症状好像是爱上我了!我在网上找医院挂号,换了好多个医院,挂不上号,全部是约满!深圳医疗资源竟然也是这样。真是见鬼啦!

症状

  • 咳嗽+
  • 嗓子不舒服
  • 鼻塞
  • 流鼻涕

2023-01-05

今天我以为症状会有所减轻,为什么会有这样的想法呢?因为早上没有怎么咳嗽,到下班之前都没有咳嗽的那么厉害,看上去好像是减轻了症状。不过最后这最终是一厢情愿了,晚上咳嗽还是那么厉害。鼻塞、流鼻涕、嗓子还是不舒服。今天晚上又新增了症状,打喷嚏、流鼻血。这样看来应该是加重了!今天又继续昨天的挂号行为,找了一些医院,很遗憾依然是约不到。真难呀!希望不会往不好方向发展,我还不想挂壁呀!今天新买了一款止咳的药是《肺宁颗粒》,希望换药后有一个减轻。

症状

  • 咳嗽+
  • 嗓子不舒服
  • 鼻塞
  • 流鼻涕
  • 打喷嚏
  • 流鼻血

2023-01-06

今天白天的咳嗽症状有点减轻,基本没有这么咳嗽啦。应该是一个好的开始,其他症状依然存在。鼻塞、流鼻滴、流鼻血、嗓子不舒服依然还有。运动稍微有点重就有点喘,感觉老了好多似的!晚上也没有这么咳嗽,在临睡觉时候看到抖音憋气实验。自己就进行憋气尝试,结果一下子咳嗽将近10分钟,这真的太危险了!

症状

  • 咳嗽-
  • 嗓子不舒服
  • 鼻塞
  • 流鼻涕-
  • 打喷嚏
  • 流鼻血

2023-01-07

今天咳嗽好像又少了,算是症状的一个减轻吧!但是除了咳嗽以外,其他的症状并没有看到任何减轻。也许咳嗽的减轻是换药带来的结果,《肺宁颗粒》也许是有用的。闭塞、流鼻滴、流鼻血症状还是持续存在。现在也预约不到医院的号,现在症状有所减轻就继续观察吧!如果可以就去检查一下,都十好几天了。希望越来越好!明天继续观察吧!

症状

  • 咳嗽-
  • 嗓子不舒服
  • 鼻塞
  • 流鼻涕-
  • 流鼻血

2023-01-08

今天咳嗽的症状又有所减少。新换的《肺宁颗粒》应该是有效的。虽然咳嗽有所好转,但是其他症状并没有见到减轻。鼻塞、流鼻涕、嗓子不舒服、流鼻血等还没有减轻。鼻塞也许是新冠引起鼻炎复发,鼻塞也导致右耳出现了一些问题,应该是中耳炎。可能还是要去医院才行!真让人厌烦呀!

症状

  • 咳嗽-
  • 嗓子不舒服
  • 鼻塞
  • 流鼻涕-
  • 流鼻血

2023-01-09

今天咳嗽继续减轻,这已经算一个好的开始了!不过现在已经没有药了,《肺宁颗粒》已经搞完一盒了,这药还是挺贵的。至于其他症状并没有什么特别大的减轻,流鼻血有所好转,鼻塞、流鼻滴、嗓子不舒服还没有什么改变,这样下去鼻塞的并发症就要来了!哎!

症状

  • 咳嗽-
  • 嗓子不舒服
  • 鼻塞
  • 流鼻涕-
  • 流鼻血-

2023-01-10

今天是春节前最后一个天上班,明天就回家了!今天咳嗽症状相比昨天差不多,咳嗽的次数不是很多。其他症状还是存在,并没有任何减轻。

症状

  • 咳嗽-
  • 嗓子不舒服
  • 鼻塞
  • 流鼻涕-
  • 流鼻血-

2023-01-11

今天老早就赶车回家。今天上午基本也没有什么咳嗽。相对来说还是不错的。其他症状也没有减轻。晚上不晓得什么原因有咳嗽了起来,明天尽快去医院看一下,不拖啦!

症状

  • 咳嗽+
  • 嗓子不舒服
  • 鼻塞
  • 流鼻涕-
  • 流鼻血-

总结



转载请注明:清风博客 » 疑似新冠感染

]]>
https://www.skyfinder.cc/2022/12/25/%e7%96%91%e4%bc%bc%e6%96%b0%e5%86%a0%e6%84%9f%e6%9f%93/feed/ 0
Chrome浏览器中的XPath https://www.skyfinder.cc/2022/12/04/chrome%e6%b5%8f%e8%a7%88%e5%99%a8%e4%b8%ad%e7%9a%84xpath/ https://www.skyfinder.cc/2022/12/04/chrome%e6%b5%8f%e8%a7%88%e5%99%a8%e4%b8%ad%e7%9a%84xpath/#respond Sat, 03 Dec 2022 16:52:11 +0000 https://www.skyfinder.cc/?p=4030 背景

某一个应用自动在网页上获取一些文本内容,本来是通过document.querySelector来找指定节点。经过一段时间网页貌似升级了,一些节点的class属性的值会出现随机的变动,每次class属性的值都会不一样。最初的方式就失去了作用,根据节点内容的分析发现可以通过xpath来获取。曾经在IE浏览器上使用过XPath,并且API相当简单。在非IE浏览器上貌似没有这么好用。以下内容在Chrome浏览器进行尝试,经过测试可以完成自己的预期工作。

浏览器支持

Mozilla是根据DOM标准来实现对XPath的支持的。DOM Level 3附加标准DOM Level 3 XPath定义了用于在DOM中计算XPath表达式的接口。遗憾的是,这个标准要比微软直观的方式复杂得多。虽然有好多与XPath相关的对象,最重要的两个是:XPathEvaluatorXPathResultXPathEvaluator利用方法evaluate()计算XPath表达式。

evaluate()方法有五个参数:XPath表达式、上下文节点、命名空间解释程序和返回的结果的类型,同时在XPathResult中存放结果(通常为null)。命名空间解释程序,只有在XML代码用到了XML命名空间时才是必要的,所以通常留空,置为null。返回结果的类型,可以是以下十个常量值之一

参数解释
XPathResult.ANY_TYPE返回符合XPath表达式类型的数据
XPathResult.ANY_UNORDERED_NODE_TYPE返回匹配节点的节点集合,但顺序可能与文档中的节点的顺序不匹配
XPathResult.BOOLEAN_TYPE返回布尔值
XPathResult.FIRST_ORDERED_NODE_TYPE返回只包含一个节点的节点集合,且这个节点是在文档中第一个匹配的节点
XPathResult.NUMBER_TYPE返回数字值
XPathResult.ORDERED_NODE_ITERATOR_TYPE返回匹配节点的节点集合,顺序为节点在文档中出现的顺序。这是最常用到的结果类型
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE返回节点集合快照,在文档外捕获节点,这样将来对文档的任何修改都不会影响这个节点列表。节点集合中的节点与它们出现在文档中的顺序一样
XPathResult.STRING_TYPE返回字符串值
XPathResult.UNORDERED_NODE_ITERATOR_TYPE返回匹配节点的节点集合,不过顺序可能不会按照节点在文档中出现的顺序排列
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE返回节点集合快照,在文档外捕获节点,这样将来对文档的任何修改都不会影响这个节点列表。节点集合中的节点和文档中原来的顺序不一定一样。

JavaScript实现XPath选择节点


function xpathQuery(xpath){
  let resultXpath = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
  let result=[];
  if(resultXpath){
     let item;
     while(item=resultXpath.iterateNext())
     {
        result.push(item);
     }
  } 
  return result;
}

xpathQuery("//div[@class='container-body']/div[contains(@class,'order-item')]");
JavaScript 使用XPath



转载请注明:清风博客 » Chrome浏览器中的XPath

]]>
https://www.skyfinder.cc/2022/12/04/chrome%e6%b5%8f%e8%a7%88%e5%99%a8%e4%b8%ad%e7%9a%84xpath/feed/ 0
Javascript判断当前页面是否处于激活状态 https://www.skyfinder.cc/2022/11/21/javascript%e5%88%a4%e6%96%ad%e5%bd%93%e5%89%8d%e9%a1%b5%e9%9d%a2%e6%98%af%e5%90%a6%e5%a4%84%e4%ba%8e%e6%bf%80%e6%b4%bb%e7%8a%b6%e6%80%81/ https://www.skyfinder.cc/2022/11/21/javascript%e5%88%a4%e6%96%ad%e5%bd%93%e5%89%8d%e9%a1%b5%e9%9d%a2%e6%98%af%e5%90%a6%e5%a4%84%e4%ba%8e%e6%bf%80%e6%b4%bb%e7%8a%b6%e6%80%81/#respond Mon, 21 Nov 2022 05:44:00 +0000 https://www.skyfinder.cc/?p=4020 背景

现有一个考试项目,当在浏览器进行考试时需要判断用户是否存在切屏,如果切屏就对当前考试进行自动强制交卷。浏览器中可通过window对象的onbluronfocus判断,或者documenthidden属性判断。

获取焦点(onfocus)和失去焦点(onblur)

关于是否失焦点,浏览器对象有onfocus onblur事件可以监听。但是触发这两个事件的前提是页面之前是获取焦点的,就是说要是激活的。也就是说页面刚刚渲染完,用户在没有页面上任何操作时,页面是不会正常监听这两个事件的;或者页面在打开状态下,但是触发了onblur之后并无页面操作的情况下也不会正常监听这两个事件。直到,用户操作页面触发focus,之后离开页面才会触发blur,再次点击到当前页面时才会触发focus,如此反复都会触发相应的事件。

onblur

  • chrome浏览器下,点击console面板也会触发blur事件,前提是之前是focus的状态。
  • 页面最小化;
  • 浏览器切换tab页面;
  • 页面中的任何弹窗;
  • focus状态下切换到其他应用

onfocus

  • 用户存在页面操作(包括页面中js脚本运行。如页面加载完无js运行,用户无操作,则不会触发 );
  • 事件触发前提下,页面最大化;
  • 事件触发前提下,页面从其他tab页切换回当前页面;

onblur与onfocus示例

  
//离开          
window.onblur = function () {
         console&&console.log("切屏了onblur");
 };
 //回来
 window.onfocus = function () {
        console&&console.log("切屏了onfocus");
  };

HTML5新增API

HTML5新增的API, visibilitychange, document.hidden, document.visibilityState来实现相关内容。

visibilitychange

  • 浏览器支持 visibilitychangeHTML5API所以支持持最新浏览器 Chrome, Firefox, IE10+
  • addEventListener添加事件,当页面发生改变就会触发

老版本浏览器如果失效需要添加前缀

  • mozvisibilitychange 火狐
  • msvisibilitychange IE
  • webkitvisibilitychange 谷歌,Safari

document.hidden

hiddendocument的属性,可以判断页面是否显示的是当前的页面。visibilitychange事件就是触发页面可见的事件。当然不同的浏览器内核记得要加前缀。表示页面处于非激活状态,反之处于激活状态。

  • 页面最小化
  • 页面在后台运行
  • 切换tab栏到其他页面

当其hiddenfalse的时候就是当前网页正在被用户浏览,其值为ture就是当前网页进入后台。

document.visibilityState

  • visible 是浏览器当前激活,窗口不是最小化状态
  • hidden 不是当前激活,或者窗口最小化了
  • prerender 重新生成,对用户不可见

visibilitychange事件示例

        
var hiddenProperty = 'hidden' in document ? 'hidden' :
            'webkitHidden' in document ? 'webkitHidden' :
                'mozHidden' in document ? 'mozHidden':null;
        var visibilityChangeEvent = hiddenProperty.replace(/hidden/i, 'visibilitychange');
        var onVisibilityChange = function () {
            if (!document[hiddenProperty]) {
                console && console.log('页面激活');
            } else {
                console && console.log('页面非激活');
            }
        }
        document.addEventListener(visibilityChangeEvent, onVisibilityChange);

以上两种方式在某些情况下存在一定差异,为了实现一些需求,可能需要组合使用。



转载请注明:清风博客 » Javascript判断当前页面是否处于激活状态

]]>
https://www.skyfinder.cc/2022/11/21/javascript%e5%88%a4%e6%96%ad%e5%bd%93%e5%89%8d%e9%a1%b5%e9%9d%a2%e6%98%af%e5%90%a6%e5%a4%84%e4%ba%8e%e6%bf%80%e6%b4%bb%e7%8a%b6%e6%80%81/feed/ 0
在非HTTPS站点中使用Content Security Policy引发的问题 https://www.skyfinder.cc/2022/10/25/csp/ https://www.skyfinder.cc/2022/10/25/csp/#respond Tue, 25 Oct 2022 08:40:42 +0000 https://www.skyfinder.cc/?p=4011 背景

有一个客户需要对以前老项目部分功能进行升级,需要升级页面按照最新版本的内容进行更新,测试发现页面无法加载。F12使用开发者工具发现所有资源文件异常,所有的资源文件竟然自动将HTTP协议换成 HTTPS 协议。异常信息如下图所示:

资源加载HTTPS错误

项目目前部署是非HTTPS的,很奇怪为什么会自动转换为HTTPS资源。经过排查在异常页面中发现了问题,在head节点下发现以下代码


<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">

以上内容会将HTTP协议自动转换为HTTPS协议。

Content-Security-Policy

Content-Security-PolicyCSP)允许站点管理者控制用户代理能够为指定的页面加载哪些资源。除了少数例外情况,设置的政策主要涉及指定服务器的源和脚本结束点。这将帮助防止跨站脚本攻击(Cross-Site Script)。

CSP 的实质就是白名单制度,大大增强了网页的安全性。攻击者即使发现了漏洞,也没法注入脚本,除非还控制了一台列入了白名单的可信主机。

两种方法可以启用 CSP。一种是通过 HTTP 头信息的Content-Security-Policy的字段。

另一种是通过网页的<meta>标签。

  • 脚本:只信任当前域名
  • <object>标签:不信任任何URL,即不加载任何资源
  • 样式表:只信任cdn.example.orgthird-party.org
  • 框架(frame):必须使用HTTPS协议加载
  • 其他资源:没有限制

限制选项

资源加载限制

以下选项限制各类资源的加载。

  • script-src:外部脚本
  • style-src:样式表
  • img-src:图像
  • media-src:媒体文件(音频和视频)
  • font-src:字体文件
  • object-src:插件(比如 Flash
  • child-src:框架
  • frame-ancestors:嵌入的外部资源(比如<frame><iframe><embed><applet>
  • connect-srcHTTP 连接(通过 XHRWebSocketsEventSource等)
  • worker-srcworker脚本
  • manifest-src:manifest 文件

default-src

default-src用来设置上面各个选项的默认值。


Content-Security-Policy: default-src 'self

上面代码限制所有的外部资源,都只能从当前域名加载。

如果同时设置某个单项限制(比如font-src)和default-src,前者会覆盖后者,即字体文件会采用font-src的值,其他资源依然采用default-src的值。

URL 限制

有时,网页会跟其他 URL 发生联系,这时也可以加以限制。

  • frame-ancestors:限制嵌入框架的网页
  • base-uri:限制<base#href>
  • form-action:限制<form#action>

其他限制

其他一些安全相关的功能,也放在了 CSP 里面。

  • block-all-mixed-contentHTTPS 网页不得加载 HTTP 资源(浏览器已经默认开启)
  • upgrade-insecure-requests:自动将网页上所有加载外部资源的 HTTP 链接换成 HTTPS 协议
  • plugin-types:限制可以使用的插件格式
  • sandbox:浏览器行为的限制,比如不能有弹出窗口等。

参考

https://www.ruanyifeng.com/blog/2016/09/csp.html



转载请注明:清风博客 » 在非HTTPS站点中使用Content Security Policy引发的问题

]]>
https://www.skyfinder.cc/2022/10/25/csp/feed/ 0
使用ABP框架UI找不到文件的异常 https://www.skyfinder.cc/2022/10/05/abp-install-libs/ https://www.skyfinder.cc/2022/10/05/abp-install-libs/#respond Wed, 05 Oct 2022 14:03:03 +0000 https://www.skyfinder.cc/?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.skyfinder.cc/2022/10/05/abp-install-libs/feed/ 0
.Net Core获取Window系统机器码 https://www.skyfinder.cc/2022/10/04/dot-net-core-machinecode/ https://www.skyfinder.cc/2022/10/04/dot-net-core-machinecode/#respond Tue, 04 Oct 2022 06:27:45 +0000 https://www.skyfinder.cc/?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.skyfinder.cc/2022/10/04/dot-net-core-machinecode/feed/ 0
判断登录的QQ是否已经加入指定的QQ群之二 https://www.skyfinder.cc/2022/09/25/check-qq-in-group/ https://www.skyfinder.cc/2022/09/25/check-qq-in-group/#comments Sun, 25 Sep 2022 10:32:10 +0000 https://www.skyfinder.cc/?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.skyfinder.cc/2022/09/25/check-qq-in-group/feed/ 6
.NET CORE实现SHA1 https://www.skyfinder.cc/2022/09/24/net-core-sha1/ https://www.skyfinder.cc/2022/09/24/net-core-sha1/#comments Sat, 24 Sep 2022 14:34:21 +0000 https://www.skyfinder.cc/?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.skyfinder.cc/2022/09/24/net-core-sha1/feed/ 2
Visual Studio Code(VS Code)意外放大后恢复设置 https://www.skyfinder.cc/2022/09/20/visual-studio-code-zoom-level/ https://www.skyfinder.cc/2022/09/20/visual-studio-code-zoom-level/#respond Tue, 20 Sep 2022 01:00:00 +0000 https://www.skyfinder.cc/?p=3941 背景

使用VS Code的时候,自己没有注意到底操作了什么导致VS Code的字体整个放大了好多。原本以为关闭后重新打开就会恢复,结果还是没有任何效果。效果如下:

Visual Studio Code(VS Code)意外放大后恢复设置-第0张图片

经过了解,应该是不小心触碰了缩放的快捷键。

解决办法

恢复的方法也比较简单,以下是第一种方式。

  • 查看
  • 外观
  • 放大、缩小、重置缩放
Zoom Level恢复第一种方式

嗯!没有错!应该是意外碰到Ctrl+=的快捷键了。

第二种方式,如下图所示:

Zoom Level恢复第二种方式第一张图片-设置
Zoom Level恢复第二种方式第二张图片
Zoom Level恢复第二种方式第三张图片-恢复

Window:Zoom Level

调整窗口的缩放级别。原始大小是 0,每次递增(例如 1)或递减(例如 -1)表示放大或缩小 20%。也可以输入小数以便以更精细的粒度调整缩放级别。

本来是不打算记录的,想了一下最终决定记录一下。俗话说得好:”好记性,不如烂笔头”。



转载请注明:清风博客 » Visual Studio Code(VS Code)意外放大后恢复设置

]]>
https://www.skyfinder.cc/2022/09/20/visual-studio-code-zoom-level/feed/ 0
使用Nginx配置资源目录达到下载目的 https://www.skyfinder.cc/2022/09/05/nginx-downlaod/ https://www.skyfinder.cc/2022/09/05/nginx-downlaod/#respond Mon, 05 Sep 2022 02:24:24 +0000 https://www.skyfinder.cc/?p=3931 使用Nginx配置资源目录达到下载目的-第0张图片

背景

实施人员临时需要在客户服务器上配置一个可下载的目录,为了可以在内网进行交换文件。

Nginx配置

       
location /resources {
          #下载的资源目录 绝对路径 最后 "/" 结尾
          alias /app/smart/logs/;
          autoindex on;
          autoindex_format html; #以html风格将目录展示在浏览器中
          autoindex_exact_size off; #切换为 off 后,以可读的方式显示文件大小,单位为 KB、MB 或者 GB
          autoindex_localtime on; #以服务器的文件时间作为显示的时间
          client_max_body_size 4048M;
          proxy_max_temp_file_size 4048M;
          proxy_send_timeout 600; #后端服务器数据回传时间(代理发送超时)
          proxy_read_timeout 600; #连接成功后,后端服务器响应时间(代理接收超时)
          
          #符合条件,直接下载
          if ($request_filename ~* ^.*?\.(txt|doc|pdf|rar|gz|zip|docx|exe|xlsx|ppt|pptx)$){
             add_header Content-Disposition attachment;
          }
        }

访问地址规则为:http://url/resources,这样就可以看到我们需要页面。示例如下:

https://www.skyfinder.cc/resources



转载请注明:清风博客 » 使用Nginx配置资源目录达到下载目的

]]>
https://www.skyfinder.cc/2022/09/05/nginx-downlaod/feed/ 0
WordPress自动对没有alt属性的img添加alt属性 https://www.skyfinder.cc/2022/08/07/wordpress-auot-add-img-alt/ https://www.skyfinder.cc/2022/08/07/wordpress-auot-add-img-alt/#respond Sun, 07 Aug 2022 01:00:00 +0000 https://www.skyfinder.cc/?p=3918 背景

使用了微软必应搜索的站长工具,看到必应搜索工具中SEO报告提示错误蛮多。主要有两个错误提示,其中一个是关于页面描述内容长短问题,另外一个就是img标签没有alt属性的问题。页面固定img标签的alt属性比较容易改,而文章内容的img标签的alt属性就没有那么方便了。

SEO错误提示

博客使用的是wordpress,其实处理起来也没有那么麻烦。一种方法就是使用现成的插件,例如:SEO Friendly Images,另一种就是在使用模板目录下,找到functions.php函数文件添加自定义函数处理。

这里提供自定义函数,内容如下:


//Wordpress判断并自动添加图片ALT属性
function image_alt($imgalt) {
	global $post;
	$title = $post->post_title;
	$imgUrl = "/<img\s*?.+?[^>]>/si";
	$isMatch=preg_match_all($imgUrl,$imgalt,$matches,PREG_SET_ORDER);
	if($isMatch) {
		if(!empty($matches) ) {
			for ($i=0; $i < count($matches); $i++) {
				$tag = $url = $matches[$i][0];
				$tag=preg_replace('/alt="\s*"/','',$tag);
				$judge = '/alt=/';
				$isMatched=preg_match($judge,$tag,$match,PREG_OFFSET_CAPTURE);
				if($isMatched) {
				   continue;
				}
				$tag=preg_replace('/<img/','<img alt="'.$title.'-第'.$i.'张图片"',$tag);
				$imgalt =str_replace($url,$tag,$imgalt);
			}
		}
	}
	return $imgalt;
}
add_filter('the_content', 'image_alt');



转载请注明:清风博客 » WordPress自动对没有alt属性的img添加alt属性

]]>
https://www.skyfinder.cc/2022/08/07/wordpress-auot-add-img-alt/feed/ 0
MYSQL修改数据库、表、字段字符集 https://www.skyfinder.cc/2022/08/05/mysql-update-character/ https://www.skyfinder.cc/2022/08/05/mysql-update-character/#respond Fri, 05 Aug 2022 01:00:00 +0000 https://www.skyfinder.cc/?p=3906 背景

由于mysql低版本暴了漏洞,所以客户就升级了mysql版本。升级到了最新版8.0.30,升级最新版后,应用就再也查不出数据,恢复到最初的版本有可以查到数据。经过确认最初数据库的表字符集为utf8,升级后这些字符集自动修改为utf8mb3。新版本数据库移除了utf8字符集,而应用本身不支持utf8mb3,所以需要修改这些字符集。经过再次确认最新版mysql以及应用都支持utf8mb4,最终决定修改字符集为utf8mb4

修改字符集

数据库


ALTER DATABASE 数据库名称 DEFAULT CHARACTER SET 编码名称 [COLLATE ...];

例如:


ALTER DATABASE activity CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci;

ALTER TABLE 表名称  CONVERT TO CHARACTER SET 编码名称 [COLLATE ...]

例如:

ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

以上修改会同时变更现有数据的字符集。如果只修改表的默认字符集,而不修改现有数据的字符集可以使用以下内容


ALTER TABLE 表名称 DEFAULT CHARACTER SET 编码名称 [COLLATE...];

例如:

ALTER TABLE users DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

字段

ALTER TABLE 表名称 CHANGE 字段名称 字段名称 CHARACTER SET 编码名称 [COLLATE ...];

例如:

ALTER TABLE users CHANGE LoginName LoginName VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

批量修改数据库表字符集

第一步

使用以下脚本并执行,产生修改数据表字符集的脚本。

SELECT 
CONCAT("ALTER TABLE ",TABLE_SCHEMA,'.',TABLE_NAME," CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;") 
AS target_tables
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA IN('数据库名称','数据库名称') 
AND TABLE_TYPE="BASE TABLE";

例如:

SELECT 
CONCAT("ALTER TABLE ",TABLE_SCHEMA,'.',TABLE_NAME," CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;") 
AS target_tables
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA IN('phpdbeqx') 
AND TABLE_TYPE="BASE TABLE";
MYSQL修改数据库、表、字段字符集-第0张图片

导出以上查询结果,执行导出的查询结果即可。如果数据量比较大,执行的时间可能会比较久。

查看字符集

查看数据库支持的字符集

select * from information_schema.character_sets;

查看数据库支持的检验规则

select * from information_schema.collations;

查看表字符集及检验规则

select * from information_schema.tables where table_name='表名' 

查看字段编码

SHOW FULL COLUMNS FROM  表名



转载请注明:清风博客 » MYSQL修改数据库、表、字段字符集

]]>
https://www.skyfinder.cc/2022/08/05/mysql-update-character/feed/ 0
.NET CORE引用Aspose.Words的异常Could not load file or assembly https://www.skyfinder.cc/2022/08/01/net-core-could-not-load-file-or-assembly/ https://www.skyfinder.cc/2022/08/01/net-core-could-not-load-file-or-assembly/#respond Mon, 01 Aug 2022 07:22:50 +0000 https://www.skyfinder.cc/?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.skyfinder.cc/2022/08/01/net-core-could-not-load-file-or-assembly/feed/ 0
解决MYSQL连接异常:SQLSTATE[HY000] [2054] https://www.skyfinder.cc/2022/07/29/mysql-sqlstate-hy000-2054/ https://www.skyfinder.cc/2022/07/29/mysql-sqlstate-hy000-2054/#respond Fri, 29 Jul 2022 03:11:41 +0000 https://www.skyfinder.cc/?p=3881 异常信息

SQLSTATE[HY000] [2054] The server requested authentication method unknown to the client[/app/thinkphp/library/think/db/Connection.php:295]

解决MYSQL连接异常:SQLSTATE[HY000] [2054]-第0张图片

原因

MySQL8中用户的认证类型(Authentication type)默认为cacheing sha2 password导致的错误,需要修改用户权限认证方式为mysql_native_password

我这个错误就是因为数据库升级导致的。

解决办法

  • 数据库降级,退回以前版本
  • 改为mysql_native_password认证方式

数据库退回以前的版本,这里不进行操作了。下面只针对认证方式改回mysql_native_password的方式。

修改mysql配置文件my.cnf

vi /etc/my.cnf

[mysqld]节点下如下内容,如果以及存在default_authentication_plugin节点,覆盖即可。

default_authentication_plugin=mysql_native_password
解决MYSQL连接异常:SQLSTATE[HY000] [2054]-第1张图片

由于更改了认证方式,所以要更改链接MYSQL用户的密码。以下以root为例

mysql -u root -p

执行以下代码完成链接MYSQL的用户密码的修改。


alter user '用户'@'%' identified with mysql_native_password by '密码';

flush privileges;

root用户为例


alter user 'root'@'%' identified with mysql_native_password by '123456';
flush privileges;

操作完毕后,重启MYSQL服务。



转载请注明:清风博客 » 解决MYSQL连接异常:SQLSTATE[HY000] [2054]

]]>
https://www.skyfinder.cc/2022/07/29/mysql-sqlstate-hy000-2054/feed/ 0
无题 https://www.skyfinder.cc/2022/05/28/%e6%97%a0%e9%a2%98/ https://www.skyfinder.cc/2022/05/28/%e6%97%a0%e9%a2%98/#respond Sat, 28 May 2022 11:39:45 +0000 https://www.skyfinder.cc/?p=3871 明月残缺空中挂,

白光倾洒覆天涯。

遥看皆是行人影,

心有归处哪有家。



转载请注明:清风博客 » 无题

]]>
https://www.skyfinder.cc/2022/05/28/%e6%97%a0%e9%a2%98/feed/ 0
.net core在Linux系统报Gdip异常的问题 https://www.skyfinder.cc/2022/04/08/net-core-linux-system-gdip-error/ https://www.skyfinder.cc/2022/04/08/net-core-linux-system-gdip-error/#respond Fri, 08 Apr 2022 01:00:00 +0000 https://www.skyfinder.cc/?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.skyfinder.cc/2022/04/08/net-core-linux-system-gdip-error/feed/ 0
使用sqlserver的排名函数实现积分排名 https://www.skyfinder.cc/2022/04/06/sql-server-rank-handle/ https://www.skyfinder.cc/2022/04/06/sql-server-rank-handle/#respond Wed, 06 Apr 2022 01:00:00 +0000 https://www.skyfinder.cc/?p=3828 背景

客户需要针对用户的积分进行排名,按照积分的多少降序进行。为了更快更好的满足客户需求,就采取了SQL Server已有的排名函数RANKDENSE_RANK来实现

RANK

返回结果集的分区内每行的排名。 行的排名是相关行之前的排名数加一。

ROW_NUMBER RANK 类似。 ROW_NUMBER 按顺序对所有行进行编号(例如: 1、2、3、4、5)。 RANK 为相应关联提供相同的数值(例如: 1、2、2、4、5)。

RANK是运行查询时计算出的临时值

语法


RANK ( ) OVER ( [ partition_by_clause ] order_by_clause )
如果两个或多个行与一个排名关联,则每个关联行将得到相同的排名

DENSE_RANK

此函数返回结果集分区中每行的排名,排名值没有间断。 特定行的排名等于该特定行之前不同排名值的数量加一。

语法

DENSE_RANK ( ) OVER ( [ <partition_by_clause> ] < order_by_clause > )
如果两个或更多行在同一分区中具有相同的排名值,那么每个行将获得相同的排名

积分排名实现

数据准备

-- 创建积分表
Create table integral
(
  userUid varchar(36),
  integralScore decimal(18,2)
);
-- 创建用户表
Create table UserInfo
(
 userUid varchar(36),
 user_name varchar(100)
);
--查询
with integral AS
(
   select userUid, ISNULL(SUM(integralScore), 0) as integralScore  from integral  group by userUid
)
,
ranks AS(
select  rank() OVER(order by integralScore desc) as ranks,integral.integralScore,UserInfo.user_name from  integral Inner Join UserInfo  on integral.userUid=UserInfo.userUid
  
)
select  * from ranks;
使用sqlserver的排名函数实现积分排名-第0张图片

rank函数实现排名,排名不连续,排名会跳过序号。如上图所示。此种情况可以使用dense_rank函数来解决,这样排名需要就会连续出现。


with integral AS
(
   select userUid, ISNULL(SUM(integralScore), 0) as integralScore  from integral  group by userUid
)
,
ranks AS(
select  dense_rank() OVER(order by integralScore desc) as ranks,integral.integralScore,UserInfo.user_name from  integral Inner Join UserInfo  on integral.userUid=UserInfo.userUid
)
select  * from ranks;
使用sqlserver的排名函数实现积分排名-第1张图片

当使用dense_rank函数排名后,就完美避免了排名跳编号的问题。



转载请注明:清风博客 » 使用sqlserver的排名函数实现积分排名

]]>
https://www.skyfinder.cc/2022/04/06/sql-server-rank-handle/feed/ 0
C#使用正则表达式移除字串符前后指定的字符串 https://www.skyfinder.cc/2022/04/04/csharp-regex-trim-remove/ https://www.skyfinder.cc/2022/04/04/csharp-regex-trim-remove/#respond Mon, 04 Apr 2022 15:49:55 +0000 https://www.skyfinder.cc/?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.skyfinder.cc/2022/04/04/csharp-regex-trim-remove/feed/ 0