来源: 宝塔面板搭建自己的网站,并发布公网远程访问_宝塔本地测试-CSDN博客
在layui中使用 jquery 触发select 的 change事件无效(转载)-CSDN博客
来源: 在layui中使用 jquery 触发select 的 change事件无效(转载)-CSDN博客
使用layui.use监听select事件
在使用layui框架时,原生的js onchange无法起效。要使用
layui.use([‘layer’, ‘JQuery’, ‘form’], function () {
var layer = layui.layer,
$ = layui.JQuery,
form = layui.form;
<select lay-filter="demo" lay-verify="required">
<script>
layui.use(['layer', 'jquery', 'form'], function () {
var layer = layui.layer,
$ = layui.jquery,
form = layui.form;
form.on('select(demo)', function(data){
if(data.value == 1){
$("#searchSessionNum").attr("disabled","true");
form.render('select');
}else{
$("#searchSessionNum").removeAttr("disabled");
form.render('select');//select是固定写法 不是选择器
}
});
});
</script>
Failed to load resource: net::ERR_CACHE_READ_FAILURE解决办法-CSDN博客
来源: Failed to load resource: net::ERR_CACHE_READ_FAILURE解决办法-CSDN博客
在使用elasticsearch分词插件时,使用kibana作为客户端管理,突然报错Kibana did not load properly. Check the server output for more information.
打开后台console查看报错Failed to load resource: net::ERR_CACHE_READ_FAILURE。缓存读取失败。
原因是我之前清空了浏览器的缓存,又重启了电脑,导致kibana读取缓存失败。
解决办法:强制再刷新下浏览器缓存就好了。
————————————————
版权声明:本文为CSDN博主「ghjzzhg」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ghjzzhg/article/details/103009276
C#/.NET/.NET Core优秀项目和框架2023年11月简报 - 追逐时光者 - 博客园
来源: C#/.NET/.NET Core优秀项目和框架2023年11月简报 – 追逐时光者 – 博客园
前言
公众号每月定期推广和分享的C#/.NET/.NET Core优秀项目和框架(公众号每周至少推荐两个优秀的项目和框架当然节假日除外),公众号推文有项目和框架的介绍、功能特点以及部分截图等(打不开或者打开GitHub很慢的同学可以优先查看公众号推文,文末一定会附带项目和框架源码地址)。注意:排名不分先后,都是十分优秀的开源项目和框架,每周定期更新分享(欢迎关注公众号:追逐时光者,第一时间获取每周精选分享资讯🔔)。
- 📈C# GitHub趋势榜
- 🎁C#/.NET/.NET Core项目宝库组织
- 🔔C#/.NET/.NET Core优秀项目和框架精选
- 🏗️C#/.NET/.NET Core优秀项目和框架Issues
- 📰C#/.NET/.NET Core优秀项目和框架公众号月刊
简报GitHub开源地址:https://github.com/YSGStudyHards/DotNetGuide/blob/main/docs/DotNet/DotNetProjectMonthly.md
CAP
- 项目简介: CAP 是一个基于 .NET Standard 的 C# 库,它是一种处理分布式事务的解决方案,同样具有 EventBus 的功能,它具有轻量级、易使用、高性能等特点。CAP 是一个EventBus,同时也是一个在微服务或者SOA系统中解决分布式事务问题的一个框架。它有助于创建可扩展,可靠并且易于更改的微服务系统。
- 项目源码地址: https://github.com/dotnetcore/CAP
- 公众号详细介绍: https://mp.weixin.qq.com/s/ONM9bLKidVCS4pAwJbG9tg


ZEQP.WMS
- 项目简介: ZEQP.WMS仓储管理系统使用 Colder.Admin.AntdVue框架进行功能开发,支持Windows、Linux、macOS多操作系统运行。当前使用的SDK版本是.Net Core 3.1,数据库支持MSSQL/MySQL。
- 项目源码地址: https://github.com/awesomedotnetcore/WMS/
- 公众号详细介绍: https://mp.weixin.qq.com/s/oSWqG0JZpdlckOOCWwIQow



HandyControl
- 项目简介: HandyControl是一套WPF控件库,它几乎重写了所有原生样式,同时包含80余款自定义控件。使用HandyControl你可以轻松地创建一个美观的WPF应用程序,从而大大提高开发效率。
- 项目源码地址: https://github.com/handyOrg/HandyControl
- 公众号详细介绍: https://mp.weixin.qq.com/s/6OcSyquHutFOm5IWcgLLBQ



NETCore.Encrypt
- 项目简介: NETCore.Encrypt是.NET Core加密解密工具类库,包括AES、RSA、MD5、SHA1、DES、SHA256、SHA384、SHA512等更多功能。
- 项目源码地址: https://github.com/myloveCc/NETCore.Encrypt
- 公众号详细介绍: https://mp.weixin.qq.com/s/Z7IcCAqnsKXksNgDUXykag


WinMemoryCleaner
- 项目简介: 一款.NET开源的小巧、智能、免费的Windows内存清理工具(使用Windows内存清理工具来优化内存,这样不必浪费时间去重新启动电脑)。
- 项目源码地址: https://github.com/IgorMundstein/WinMemoryCleaner
- 公众号详细介绍: https://mp.weixin.qq.com/s/y3hqs9T_i5ApgjZEHeoxJg



Hello算法
- 项目简介: Hello算法一个开源免费、新手友好的数据结构与算法入门教程。全书采用动画图解,内容清晰易懂、学习曲线平滑,引导初学者探索数据结构与算法的知识地图。源代码可一键运行,帮助读者在练习中提升编程技能,了解算法工作原理和数据结构底层实现。支持 Java, C++, Python, Go, JS, TS, C#, Swift, Rust, Dart, Zig 等语言。
- 项目源码地址: https://github.com/krahets/hello-algo
- 公众号详细介绍: https://mp.weixin.qq.com/s/9lb5iu6tGNiSGcIrf7fQ3A




PaddleOCRSharp
- 项目简介: PaddleOCRSharp 是一个基于百度飞桨PaddleOCR的.NET版本OCR工具类库。项目核心组件PaddleOCR.dll,由C++编写,根据百度飞桨PaddleOCR的C++代码修改并优化而成。目前已经支持C++、.NET、Python、Golang、Rust等开发语言的直接API接口调用。项目包含文本识别、文本检测、表格识别功能。本项目针对小图识别不准的情况下做了优化,比飞桨原代码识别准确率有所提高。包含总模型仅8.6M的超轻量级中文OCR,单模型支持中英文数字组合识别、竖排文本识别、长文本识别。同时支持中英文、纯英文以及多种语言文本检测识别。
- 项目源码地址: https://gitee.com/raoyutian/paddle-ocrsharp
- 公众号详细介绍: https://mp.weixin.qq.com/s/9F_rSB8Wm69jLdgsH4ufvg



MrHuo.OAuth
- 项目简介: MrHuo.OAuth是.NET项目集成OAuth2登录最全面的、最方便的框架,集成了国内外大部分平台(.NET Core 项目或 .NET Framework 4.6 项目均可使用)。
- 项目源码地址: https://github.com/mrhuo/MrHuo.OAuth
- 公众号详细介绍: https://mp.weixin.qq.com/s/2wTmSz-qPOWPCipIrIWvkg



CoreShop
- 项目简介: 核心商城系统(CoreShop) 是基于 ASP.NET 7.0、Uni-App开发、支持可视化布局的小程序商城系统;前后端分离,支持分布式部署,跨平台运行;拥有分销、代理、团购秒杀、接龙、拼团、直播、优惠券、自定义表单等众多营销功能,拥有完整SKU、下单、售后、物流流程,支持可视化自定义首页模块布局效果。
- 项目源码地址: https://github.com/CoreUnion/CoreShop
- 公众号详细介绍: https://mp.weixin.qq.com/s/iRxmWUXrqArZD_Ax4i6wwg




ToastFish
- 项目简介: 一个C#开源且免费的能利用Windows通知栏背单词的软件,可以让你在上班、上课等恶劣环境下安全隐蔽地背单词(利用摸鱼时间背单词的软件)。
- 项目源码地址: https://github.com/Uahh/ToastFish
- 公众号详细介绍: https://mp.weixin.qq.com/s/CS4NxQMN18LxyBIoEx4RIA





DotNetGuide技术社区交流群
- DotNetGuide技术社区是一个面向.NET开发者的开源技术社区,旨在为开发者们提供全面的C#/.NET/.NET Core相关学习资料、技术分享和咨询、项目推荐、招聘资讯和解决问题的平台。
- 在这个社区中,开发者们可以分享自己的技术文章、项目经验、遇到的疑难技术问题以及解决方案,并且还有机会结识志同道合的开发者。
- 我们致力于构建一个积极向上、和谐友善的.NET技术交流平台,为广大.NET开发者带来更多的价值和成长机会。
.NET8 依赖注入 - xiaolipro - 博客园
来源: .NET8 依赖注入 – xiaolipro – 博客园
依赖注入(Dependency Injection,简称DI)是一种设计模式,用于解耦组件(服务)之间的依赖关系。它通过将依赖关系的创建和管理交给外部容器来实现,而不是在组件(服务)内部直接创建依赖对象。
咱就是通过 IServiceCollection 和 IServiceProvider 来实现的,他们直接被收入到了runtime libraries,在整个.NET平台下通用!
3.1 ServiceCollection
IServiceCollection 本质是一个 ServiceDescriptor 而 ServiceDescriptor 则是用于描述服务类型,实现和生命周期
public interface IServiceCollection :
IList<ServiceDescriptor>,
ICollection<ServiceDescriptor>,
IEnumerable<ServiceDescriptor>,
IEnumerable;
官方提供一些列拓展帮助我们向服务容器中添加服务描述,具体在 ServiceCollectionServiceExtensions
builder.Services.AddTransient<StudentService>();
builder.Services.AddKeyedTransient<IStudentRepository, StudentRepository>("a");
builder.Services.AddKeyedTransient<IStudentRepository, StudentRepository2>("b");
builder.Services.AddTransient<TransientService>();
builder.Services.AddScoped<ScopeService>();
builder.Services.AddSingleton<SingletonService>();
3.2 ServiceProvider
IServiceProvider 定义了一个方法 GetService,帮助我们通过给定的服务类型,获取其服务实例
public interface IServiceProvider
{
object? GetService(Type serviceType);
}
下面是 GetService 的默认实现(如果不给定engine scope,则默认是root)
public object? GetService(Type serviceType) => GetService(ServiceIdentifier.FromServiceType(serviceType), Root);
也就是
internal object? GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope)
{
if (_disposed)
{
ThrowHelper.ThrowObjectDisposedException();
}
// 获取服务标识符对应的服务访问器
ServiceAccessor serviceAccessor = _serviceAccessors.GetOrAdd(serviceIdentifier, _createServiceAccessor);
// 执行解析时的hock
OnResolve(serviceAccessor.CallSite, serviceProviderEngineScope);
DependencyInjectionEventSource.Log.ServiceResolved(this, serviceIdentifier.ServiceType);
// 通过服务访问器提供的解析服务,得到服务实例
object? result = serviceAccessor.RealizedService?.Invoke(serviceProviderEngineScope);
System.Diagnostics.Debug.Assert(result is null || CallSiteFactory.IsService(serviceIdentifier));
return result;
}
其中,服务标识符 ServiceIdentifier 其实就是包了一下服务类型,和服务Key(为了.NET8的键化服务)
internal readonly struct ServiceIdentifier : IEquatable<ServiceIdentifier>
{
public object? ServiceKey { get; }
public Type ServiceType { get; }
}
显而易见的,我们的服务解析是由 serviceAccessor.RealizedService 提供,而创建服务访问器 serviceAccessor 只有一个实现 CreateServiceAccessor
private ServiceAccessor CreateServiceAccessor(ServiceIdentifier serviceIdentifier)
{
// 通过 CallSiteFactory 获取服务的调用点(CallSite),这是服务解析的一个表示形式。
ServiceCallSite? callSite = CallSiteFactory.GetCallSite(serviceIdentifier, new CallSiteChain());
// 如果调用站点不为空,则继续构建服务访问器。
if (callSite != null)
{
DependencyInjectionEventSource.Log.CallSiteBuilt(this, serviceIdentifier.ServiceType, callSite);
// 触发创建调用站点的相关事件。
OnCreate(callSite);
// 如果调用站点的缓存位置是根(Root),即表示这是一个单例服务。
if (callSite.Cache.Location == CallSiteResultCacheLocation.Root)
{
// 直接拿缓存内容
object? value = CallSiteRuntimeResolver.Instance.Resolve(callSite, Root);
return new ServiceAccessor { CallSite = callSite, RealizedService = scope => value };
}
// 通过引擎解析
Func<ServiceProviderEngineScope, object?> realizedService = _engine.RealizeService(callSite);
return new ServiceAccessor { CallSite = callSite, RealizedService = realizedService };
}
// 如果调用点为空,则它的实现服务函数总是返回 null。
return new ServiceAccessor { CallSite = callSite, RealizedService = _ => null };
}
3.2.1 ServiceProviderEngine
ServiceProviderEngine 是服务商解析服务的执行引擎,它在服务商被初始化时建立。有两种引擎,分别是动态引擎和运行时引擎,在 NETFRAMEWORK || NETSTANDARD2_0 默认使用动态引擎。
private ServiceProviderEngine GetEngine()
{
ServiceProviderEngine engine;
#if NETFRAMEWORK || NETSTANDARD2_0
engine = CreateDynamicEngine();
#else
if (RuntimeFeature.IsDynamicCodeCompiled && !DisableDynamicEngine)
{
engine = CreateDynamicEngine();
}
else
{
// Don't try to compile Expressions/IL if they are going to get interpreted
engine = RuntimeServiceProviderEngine.Instance;
}
#endif
return engine;
[UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode",
Justification = "CreateDynamicEngine won't be called when using NativeAOT.")] // see also https://github.com/dotnet/linker/issues/2715
ServiceProviderEngine CreateDynamicEngine() => new DynamicServiceProviderEngine(this);
}
由于.NET Aot技术与dynamic技术冲突,因此Aot下只能使用运行时引擎,但动态引擎在大多情况下仍然是默认的。
动态引擎使用了emit技术,这是一个动态编译技术,而aot的所有代码都需要在部署前编译好,因此运行时无法生成新的代码。那运行时引擎主要使用反射,目标是在不牺牲太多性能的情况下,提供一个在aot环境中可行的解决方案。
我们展开动态引擎来看看它是如何解析服务的。
public override Func<ServiceProviderEngineScope, object?> RealizeService(ServiceCallSite callSite)
{
// 定义一个局部变量来跟踪委托被调用的次数
int callCount = 0;
return scope =>
{
// 当委托被调用时,先使用CallSiteRuntimeResolver.Instance.Resolve方法来解析服务。这是一个同步操作,确保在编译优化之前,服务可以被正常解析。
var result = CallSiteRuntimeResolver.Instance.Resolve(callSite, scope);
// 委托第二次被调用,通过UnsafeQueueUserWorkItem在后台线程上启动编译优化
if (Interlocked.Increment(ref callCount) == 2)
{
// 将一个工作项排队到线程池,但不捕获当前的执行上下文。
_ = ThreadPool.UnsafeQueueUserWorkItem(_ =>
{
try
{
// 用编译优化后的委托替换当前的服务访问器,主要用到emit/expression技术
_serviceProvider.ReplaceServiceAccessor(callSite, base.RealizeService(callSite));
}
catch (Exception ex)
{
DependencyInjectionEventSource.Log.ServiceRealizationFailed(ex, _serviceProvider.GetHashCode());
Debug.Fail($"We should never get exceptions from the background compilation.{Environment.NewLine}{ex}");
}
},
null);
}
return result;
};
}
这个实现的关键思想是,第一次解析服务时使用一个简单的运行时解析器,这样可以快速返回服务实例。然后,当服务再被解析,它会在后台线程上启动一个编译过程,生成一个更高效的服务解析委托。一旦编译完成,新的委托会替换掉原来的委托,以后的服务解析将使用这个新的、更高效的委托。这种方法可以在不影响应用程序启动时间的情况下,逐渐优化服务解析的性能。
3.2.2 ServiceProviderEngineScope
ServiceProviderEngineScope 闪亮登场,他是我们服务商的代言人,从定义不难看出他对外提供了服务商所具备的一切能力
internal sealed class ServiceProviderEngineScope : IServiceScope, IServiceProvider, IKeyedServiceProvider, IAsyncDisposable, IServiceScopeFactory
{
// this scope中所有实现IDisposable or IAsyncDisposable的服务
private List<object>? _disposables;
// 解析过的服务缓存(其实就是scope生命周期的服务缓存,singleton生命周期的服务缓存都直接挂在调用点上了)
internal Dictionary<ServiceCacheKey, object?> ResolvedServices { get; }
// 实锤服务商代言人
public IServiceProvider ServiceProvider => this;
// 没错啦,通过root scope我们又能继续创建无数个scope,他们彼此独立
public IServiceScope CreateScope() => RootProvider.CreateScope();
}
我们来观察他获取服务的逻辑,可以发现他就是很朴实无华的用着我们根服务商 ServiceProvider,去解析服务,那 engine scope 呢,就是 this。现在我们已经隐约可以知道engine scope,就是为了满足scope生命周期而生。而 ResolvedServices 中存的呢,就是该scope中的所有scope生命周期服务实例啦,在这个scope中他们是唯一的。
public object? GetService(Type serviceType)
{
if (_disposed)
{
ThrowHelper.ThrowObjectDisposedException();
}
return RootProvider.GetService(ServiceIdentifier.FromServiceType(serviceType), this);
}
再来看另一个核心的方法:CaptureDisposable,实现disposable的服务会被添加到 _disposables。
internal object? CaptureDisposable(object? service)
{
// 如果服务没有实现 IDisposable or IAsyncDisposable,那么不需要捕获,直接原路返回
if (ReferenceEquals(this, service) || !(service is IDisposable || service is IAsyncDisposable))
{
return service;
}
bool disposed = false;
lock (Sync)
{
if (_disposed) // 如果scope已经销毁则进入销毁流程
{
disposed = true;
}
else
{
_disposables ??= new List<object>();
_disposables.Add(service);
}
}
// Don't run customer code under the lock
if (disposed) // 这表示我们在试图捕获可销毁服务时,scope就已经被销毁
{
if (service is IDisposable disposable)
{
disposable.Dispose();
}
else
{
// sync over async, for the rare case that an object only implements IAsyncDisposable and may end up starving the thread pool.
object? localService = service; // copy to avoid closure on other paths
Task.Run(() => ((IAsyncDisposable)localService).DisposeAsync().AsTask()).GetAwaiter().GetResult();
}
// 这种case会抛出一个ObjectDisposedException
ThrowHelper.ThrowObjectDisposedException();
}
return service;
}
在engine scope销毁时,其作用域中所有scope生命周期且实现了disposable的服务(其实就是_disposable)呢,也会被一同的销毁。
public ValueTask DisposeAsync()
{
List<object>? toDispose = BeginDispose(); // 获取_disposable
if (toDispose != null)
{
// 从后往前,依次销毁服务
}
}
那么有同学可能就要问了:单例实例既然不存在root scope中,而是单独丢到了调用点上,那他是咋销毁的?压根没看到啊,那不得泄露了?
其实呀,同学们并不用担心这个问题。首先,单例服务的实例确实是缓存在调用点上,但 disable 服务仍然会被 scope 捕获呀(在下文会详细介绍)。在 BeginDispose 中的,我们会去判断,如果是 singleton case,且root scope 没有被销毁过,我们会主动去销毁喔~
if (IsRootScope && !RootProvider.IsDisposed()) RootProvider.Dispose();
3.3 ServiceCallSite
ServiceCallSite 的主要职责是封装服务解析的逻辑,它可以代表一个构造函数调用、属性注入、工厂方法调用等。DI系统使用这个抽象来表示服务的各种解析策略,并且可以通过它来生成服务实例。
internal abstract class ServiceCallSite
{
protected ServiceCallSite(ResultCache cache)
{
Cache = cache;
}
public abstract Type ServiceType { get; }
public abstract Type? ImplementationType { get; }
public abstract CallSiteKind Kind { get; }
public ResultCache Cache { get; }
public object? Value { get; set; }
public object? Key { get; set; }
public bool CaptureDisposable => ImplementationType == null ||
typeof(IDisposable).IsAssignableFrom(ImplementationType) ||
typeof(IAsyncDisposable).IsAssignableFrom(ImplementationType);
}
3.3.1 ResultCache
其中 ResultCache 定义了我们如何缓存解析后的结果
public CallSiteResultCacheLocation Location { get; set; } // 缓存位置
public ServiceCacheKey Key { get; set; } // 服务key(键化服务用的)
CallSiteResultCacheLocation 是一个枚举,定义了几个值
Root:表示服务实例应该在根级别的IServiceProvider中缓存。这通常意味着服务实例是单例的(Singleton),在整个应用程序的生命周期内只会创建一次,并且在所有请求中共享。Scope:表示服务实例应该在当前作用域(Scope)中缓存。对于作用域服务(Scoped),实例会在每个作用域中创建一次,并在该作用域内的所有请求中共享。Dispose:尽管这个名称可能会让人误解,但在ResultCache的上下文中,Dispose表示着服务是瞬态的(每次请求都创建新实例)。None:表示没有缓存服务实例。
ServiceCacheKey 结构体就是包了一下服务标识符和一个slot,用于适配多实现的
internal readonly struct ServiceCacheKey : IEquatable<ServiceCacheKey>
{
public ServiceIdentifier ServiceIdentifier { get; }
public int Slot { get; } // 那最后一个实现的slot是0
}
3.3.2 CallSiteFactory.GetCallSite
那我们来看看调用点是怎么创建的吧,其实上面已经出现过一次了:
private ServiceCallSite? CreateCallSite(ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain)
{
if (!_stackGuard.TryEnterOnCurrentStack()) // 防止栈溢出
{
return _stackGuard.RunOnEmptyStack(CreateCallSite, serviceIdentifier, callSiteChain);
}
// 获取服务标识符对应的锁,以确保在创建调用点时的线程安全。
// 是为了保证并行解析下的调用点也只会被创建一次,例如:
// C -> D -> A
// E -> D -> A
var callsiteLock = _callSiteLocks.GetOrAdd(serviceIdentifier, static _ => new object());
lock (callsiteLock)
{
// 检查当前服务标识符是否会导致循环依赖
callSiteChain.CheckCircularDependency(serviceIdentifier);
// 首先尝试创建精确匹配的服务调用站点,如果失败,则尝试创建开放泛型服务调用站点,如果还是失败,则尝试创建枚举服务调用站点。如果所有尝试都失败了,callSite将为null。
ServiceCallSite? callSite = TryCreateExact(serviceIdentifier, callSiteChain) ??
TryCreateOpenGeneric(serviceIdentifier, callSiteChain) ??
TryCreateEnumerable(serviceIdentifier, callSiteChain);
return callSite;
}
}
那服务点的创建过程我就简单概述一下啦
- 查找调用点缓存,存在就直接返回啦
- 服务标识符会被转成服务描述符
ServiceDescriptor(key化服务不指定key默认取last) - 计算
ServiceCallSite,依次是:- TryCreateExact
- 计算
ResultCache - 如果已经有实现实例了,则返回
ConstantCallSite:表示直接返回已经创建的实例的调用点。 - 如果有实现工厂,则返回
FactoryCallSite:表示通过工厂方法创建服务实例的调用点。 - 如果有实现类型,则返回
ConstructorCallSite:表示通过构造函数创建服务实例的调用点。
- 计算
- TryCreateOpenGeneric
- 根据泛型定义获取服务描述符
ServiceDescriptor - 计算
ResultCache - 使用服务标识符中的具体泛型参数来构造实现的闭合类型
- AOT兼容性测试(因为不能保证值类型泛型的代码已经生成)
- 如果成功闭合,则返回
ConstructorCallSite:表示通过构造函数创建服务实例的调用点。
- 根据泛型定义获取服务描述符
- TryCreateEnumerable
- 确定类型是
IEnumerable<T> - AOT兼容性测试(因为不能保证值类型数组的代码已经生成)
- 如果
T不是泛型类型,并且可以找到对应的服务描述符集合,则循环 TryCreateExact - 否则,方向循环 TryCreateExact,然后方向循环 TryCreateOpenGeneric
- 确定类型是
- TryCreateExact
3.4 CallSiteVisitor
好了,有了上面的了解我们可以开始探索服务解析的内幕了。服务解析说白了就是引擎围着 CallSiteVisitor 转圈圈,所谓的解析服务,其实就是访问调用点了。
protected virtual TResult VisitCallSite(ServiceCallSite callSite, TArgument argument)
{
if (!_stackGuard.TryEnterOnCurrentStack()) // 一些校验,分栈啥的
{
return _stackGuard.RunOnEmptyStack(VisitCallSite, callSite, argument);
}
switch (callSite.Cache.Location)
{
case CallSiteResultCacheLocation.Root: // 单例
return VisitRootCache(callSite, argument);
case CallSiteResultCacheLocation.Scope: // 作用域
return VisitScopeCache(callSite, argument);
case CallSiteResultCacheLocation.Dispose: // 瞬态
return VisitDisposeCache(callSite, argument);
case CallSiteResultCacheLocation.None: // 不缓存(ConstantCallSite)
return VisitNoCache(callSite, argument);
default:
throw new ArgumentOutOfRangeException();
}
}
为了方便展示,我们这里的解析器都拿运行时来说,因为内部是反射,而emit、expression实在是难以观看。
3.4.1 VisitRootCache
那我们来看看单例的情况下,是如何访问的:
protected override object? VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context)
{
if (callSite.Value is object value)
{
// Value already calculated, return it directly
return value;
}
var lockType = RuntimeResolverLock.Root;
// 单例都是直接放根作用域的
ServiceProviderEngineScope serviceProviderEngine = context.Scope.RootProvider.Root;
lock (callSite)
{
// 这里搞了个双检锁来确保在多线程环境中,同一时间只有一个线程可以执行接下来的代码块。
// Lock the callsite and check if another thread already cached the value
if (callSite.Value is object callSiteValue)
{
return callSiteValue;
}
object? resolved = VisitCallSiteMain(callSite, new RuntimeResolverContext
{
Scope = serviceProviderEngine,
AcquiredLocks = context.AcquiredLocks | lockType
});
// 捕获可销毁的服务
serviceProviderEngine.CaptureDisposable(resolved);
// 缓存解析结果到调用点上
callSite.Value = resolved;
return resolved;
}
}
好,可以看到真正解析调用点的主角出来了 VisitCallSiteMain,那这里的 CallSiteKind 上面计算 ServiceCallSite 时呢已经讲的很清楚啦,咱对号入座就行了
protected virtual TResult VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
{
switch (callSite.Kind)
{
case CallSiteKind.Factory:
return VisitFactory((FactoryCallSite)callSite, argument);
case CallSiteKind.IEnumerable:
return VisitIEnumerable((IEnumerableCallSite)callSite, argument);
case CallSiteKind.Constructor:
return VisitConstructor((ConstructorCallSite)callSite, argument);
case CallSiteKind.Constant:
return VisitConstant((ConstantCallSite)callSite, argument);
case CallSiteKind.ServiceProvider:
return VisitServiceProvider((ServiceProviderCallSite)callSite, argument);
default:
throw new NotSupportedException(SR.Format(SR.CallSiteTypeNotSupported, callSite.GetType()));
}
}
我们就看看最经典的通过构造函数创建服务实例的调用点 ConstructorCallSite,很显然就是new嘛,只不过可能构造中依赖其它服务,那就递归创建呗。easy,其它几种太简单了大家自己去看看吧。
protected override object VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
{
object?[] parameterValues;
if (constructorCallSite.ParameterCallSites.Length == 0)
{
parameterValues = Array.Empty<object>();
}
else
{
parameterValues = new object?[constructorCallSite.ParameterCallSites.Length];
for (int index = 0; index < parameterValues.Length; index++)
{
// 递归构建依赖的服务
parameterValues[index] = VisitCallSite(constructorCallSite.ParameterCallSites[index], context);
}
}
// new (xxx)
return constructorCallSite.ConstructorInfo.Invoke(BindingFlags.DoNotWrapExceptions, binder: null, parameters: parameterValues, culture: null);
}
3.4.2 VisitScopeCache
在访问单例缓存的时候呢,仅仅通过了一个double check lock就搞定了,因为人家全局的嘛,咱再来看看访问作用域缓存,会不会有什么不一样
protected override object? VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
{
// Check if we are in the situation where scoped service was promoted to singleton
// and we need to lock the root
return context.Scope.IsRootScope ?
VisitRootCache(callSite, context) :
VisitCache(callSite, context, context.Scope, RuntimeResolverLock.Scope);
}
哈哈,它果然很不一般啊,上来就来检查我们是否是 root scope。如果是这种case呢,则走 VisitRootCache。但是奇怪啊,为什么访问 scope cache,所在 engine scope 能是 root scope?
还记得 ServiceProvider 获取的服务实例的核心方法吗?engine scope 他是传进来的,如果我们给一个 root scope,自然就出现的这种case,只是这种 case 特别罕见。
internal object? GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope)
VisitCache 的同步模型写的实在是酷,我们看 RuntimeResolverLock 的枚举就两个:Scope = 1 和 Root = 2
- AcquiredLocks=Scope时
- 那 AcquiredLocks&false==0 显然成立,申请锁,也就是尝试独占改作用域的ResolvedServices
- 申请成功进入同步块,重新计算AcquiredLocks|true=1
- 如此,在该engine scope 中这条链路上的调用点都被占有,直到结束
- AcquiredLocks=Root 时
- 那显然 engine scope 也应该是 root scope,那么走
VisitRootCachecase - 在
VisitRootCache通过DCL锁住 root scope 上链路涉及的服务点,直至结束
- 那显然 engine scope 也应该是 root scope,那么走
至此我们应该不难看出这个设计的精妙之处,即在非 root scope(scope生命周期)中,scope之间是互相隔离的,没有必要像 root scope(singleton生命周期)那样,在所有scope中独占服务点。
private object? VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine
{
bool lockTaken = false;
object sync = serviceProviderEngine.Sync;
Dictionary<ServiceCacheKey, object?> resolvedServices = serviceProviderEngine.ResolvedServices;
if ((context.AcquiredLocks & lockType) == 0)
{
Monitor.Enter(sync, ref lockTaken);
}
try
{
// Note: This method has already taken lock by the caller for resolution and access synchronization.
// For scoped: takes a dictionary as both a resolution lock and a dictionary access lock.
if (resolvedServices.TryGetValue(callSite.Cache.Key, out object? resolved))
{
return resolved;
}
// scope服务的解析结果是放在engine scope的ResolvedServices上的,而非调用点
resolved = VisitCallSiteMain(callSite, new RuntimeResolverContext
{
Scope = serviceProviderEngine,
AcquiredLocks = context.AcquiredLocks | lockType
});
serviceProviderEngine.CaptureDisposable(resolved);
resolvedServices.Add(callSite.Cache.Key, resolved);
return resolved;
}
finally
{
if (lockTaken)
{
Monitor.Exit(sync);
}
}
}
3.4.3 VisitDisposeCache
我们看最后一个,也就是 Transient case
protected override object? VisitDisposeCache(ServiceCallSite transientCallSite, RuntimeResolverContext context)
{
return context.Scope.CaptureDisposable(VisitCallSiteMain(transientCallSite, context));
}
异常的简单,我们沿用了scope的设计,但是我们没有进行任何缓存行为。即,每次都去访问调用点。
【ASP.NET Core】MVC过滤器:常见用法 - 东邪独孤 - 博客园
来源: 【ASP.NET Core】MVC过滤器:常见用法 – 东邪独孤 – 博客园
前面老周给大伙伴们演示了过滤器的运行流程,大伙只需要知道下面知识点即可:
1、过滤器分为授权过滤、资源访问过滤、操作方法(Action)过滤、结果过滤、异常过滤、终结点过滤。上一次咱们没有说异常过滤和终结点过滤,不过老周后面会说的。对这些过滤器,你有印象就行了。
2、所有过滤器接口都有同步版本和异步版本。为了让伙伴不要学得太累,咱们暂时只说同步版本的。
3、过滤器的应用可以分为全局和局部。全局先运行,局部后运行。全局在应用程序初始化时配置,局部用特性类来配置。
4、实际应用中,我们不需要实现所有过滤器接口,需要啥就实现啥即可。比如,你想在 Action 调用后修改一些东西,那实现 IActionFilter 接口就好了,其他不用管。
本篇咱们的重点在于“用”,光知道是啥是不行的,得拿来用才是硬道理。
我们先做第一个练习:阻止控制器的参数从查询字符串获取数据。
什么意思呢?咱们知道,MVC 在模型绑定时,会从 N 个 ValueProvider 中提取数据值,包括 QueryString、Forms、RouteData 等。其中,QueryString 就是URL的查询字符串。比如,咱们写一个这样的控制器:
public class GameController : ControllerBase
{
[HttpGet("game/play")]
public string Play(Game g)
{
if(ModelState.IsValid == false)
{
return "你玩个寂寞";
}
return $"你正在玩{g.Year}年出的《{g.GameName}》游戏";
}
}
public class Game
{
/// <summary>
/// 游戏序列号
/// </summary>
public string? GameSerial { get; set; }
/// <summary>
/// 游戏名称
/// </summary>
public string? GameName { get; set; }
/// <summary>
/// 谁发行的
/// </summary>
public string? Publisher { get; set; }
/// <summary>
/// 哪一年发行的
/// </summary>
public int Year { get; set; }
}
这个通过 /game/play?gameserial=DDSBYCL-5K2FF&gamename=伏地魔三世&publisher=无德无能科技有限公司&year=2017 这样的URL就能传递数据给 g 参数。
这里我不做 HTML 页了,直接通过 MapGet 返回 HTML 内容。
app.MapGet("/", async (HttpContext context) =>
{
string html = """
<!DOCTYPE html>
<html>
<head>
<title>试试看</title>
<style>
label {
min-width: 100px;
display: inline-block;
}
</style>
</head>
<body>
<div>
<label for="gserial">游戏序列号:</label>
<input id="gserial" type="text" />
</div>
<div>
<label for="gname">游戏名称:</label>
<input id="gname" type="text" />
</div>
<div>
<label for="pub">发行者:</label>
<input type="text" id="pub" />
</div>
<div>
<label for="year">发行年份:</label>
<input id="year" type="text"/>
</div>
<div>
<button onclick="reqTest()">确定</button>
</div>
<p id="res"></p>
<script>
function reqTest() {
let serial= document.getElementById("gserial").value;
let name = document.getElementById("gname").value;
let pub = document.getElementById("pub").value;
let year = parseInt(document.getElementById("year").value, 10);
let result = document.getElementById("res");
const url = `/game/play?gameSerial=${serial}&gamename=${name}&publisher=${pub}&year=${year}`;
fetch(url, { method: "GET" })
.then(response => {
response.text().then(txt => {
result.innerHTML = txt;
});
});
}
</script>
</body>
</html>
""";
var response = context.Response;
response.Headers.ContentType = "text/html; charset=UTF-8";
await response.WriteAsync(html);
});
设置响应的 Content-Type 头时一定要指定字符集是 UTF-8 编码,这样可以免去 99.999% 的乱码问题。向服务器发送请求是通过 fetch 函数实现的。
比如,咱们在页面上填写:

然后点一下“确定”按钮,提交成功后服务将响应:
你正在玩2022年出的《法外狂徒大冒险》游戏
模型绑定的数据是从查询字符串提取出来的。现在,咱们写一个过滤器,阻止 QueryStringValueProvider 提供查询字符串数据。而 QueryStringValueProvider 实例是由 QueryStringValueProviderFactory 工厂类负责创建的。因此,需要写一个过滤器,在模型绑定之前删除 QueryStringValueProviderFactory 对象,这样模型绑定时就不会读取 URL 中的查询字符串了。
于是,重点就落在选用哪种过滤器。关键点是:必须在模型绑定前做这项工作。所以,Action过滤器、结果过滤器就别指望了,而且肯定不是授权过滤器,那就剩下资源过滤器了。
咱们写一个自定义的资源过滤器—— RemoveQueryStringProviderFilter,实现的接口当然是 IResourceFilter 了。
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
public class RemoveQueryStringProviderFilter : IResourceFilter
{
public void OnResourceExecuted(ResourceExecutedContext context)
{
// 空空如也
}
public void OnResourceExecuting(ResourceExecutingContext context)
{
var qsValueProviders = context.ValueProviderFactories.OfType<QueryStringValueProviderFactory>();
if (qsValueProviders != null && qsValueProviders.Any())
{
context.ValueProviderFactories.RemoveType<QueryStringValueProviderFactory>();
}
}
}
我们要做的事情是在模型绑定之前才有效,所以 OnResourceExecuted 方法不用管,留白即可。在 OnResourceExecuting 方法中,首先用 ValueProviderFactories.OfType<T> 方法找出现有的 QueryStringValueProviderFactory 对象,若找到,用 RemoveType 方法删除。
把这个过滤器应用于全局。
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(options =>
{
options.Filters.Add<RemoveQueryStringProviderFilter>();
});
var app = builder.Build();
现在,咱们再运行应用程序,输入游戏信息。

点击“确定”按钮后,发现服务未响应正确的内容。
你正在玩0年出的《》游戏
由于无法从查询字符串中提取到数据,所以返回默认属性值。为什么不是返回“你玩个寂寞”呢?因为模型绑定并没有出错,也未出现验证失败的值,只是未提供值而已,即 ModelState.IsValid 的值依然是 true 的。
下面咱们看看异常过滤器怎么用。
异常过滤器最好定义为局部的,而非全局。使用局部过滤器的好处是针对性好,全局的话你用一个万能异常处理好像过于简单。当然了,如果你的项目可以这样做,就随便用。
这里我定义一个局部的异常过滤器,通过特性方式应用到操作方法上。
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class CustExceptionFilterAttribute : Attribute, IExceptionFilter
{
public void OnException(ExceptionContext context)
{
// 看看有没有异常
if (context.Exception is null || context.ExceptionHandled)
return;
// 有异常哦,修改一下返回结果
ContentResult result = new();
result.Content = "出错啦,伙计。错误消息:" + context.Exception.Message;
// 设置返回结果
context.Result = result;
// 标记异常已处理
context.ExceptionHandled = true;
}
}
首先,context 参数是一种上下文对象,它在多上异常过滤器间共享实例(你可能实现了很多个异常过滤器)。context.Exception 属性引用的是异常类对象;注意一个有趣的属性 ExceptionHandled,它是一个 bool 类型的值,表示“我这个异常过滤器是否已处理过了”。这个有啥用呢?由于上下文是共享的,当你的某个异常过滤器设置了 ExceptionHandled 为 true,那么,其他异常过滤器也可以读这个属性。这样就可以实现:啊,原来有人处理过这个异常了,那我就不处理了。即 A 过滤器处理异常后设置为已处理,B 过滤器可以检查这个属性的值,如果没必要处理就跳过。
下面写一个控制器,并在方法成员上应用前面定义的异常过滤器。
public class AbcController : ControllerBase
{
[HttpGet("calc/add"), CustExceptionFilter]
public int Add(int x, int y)
{
int r = x + y;
if(r >= 1000)
{
throw new Exception("计算结果必须在1000以内");
}
return r;
}
}
此处我设定抛出异常的条件是:x、y 相加结果大于或等于 1000。
咱们以 GET 方式调用,URL 为 /calc/add?x=599&y=699,这样会发生异常。服务器的响应为:

最后一个例子是结果过滤器。咱们要实现在响应消息中添加自定义 Cookie。
public class SetCookieResultFilter : IResultFilter
{
public void OnResultExecuted(ResultExecutedContext context)
{
// 不能在这里写 Cookie
}
public void OnResultExecuting(ResultExecutingContext context)
{
HttpResponse response = context.HttpContext.Response;
// 设置Cookie
response.Cookies.Append("X-VER", "3.0");
}
}
这里要注意,咱们要写 Cookie 必须在 OnResultExecuting 方法中处理,不能在 OnResultExecuted 方法中写。因为当 OnResultExecuted 方法执行时,响应消息头已经被锁定了,Cookie 是通过 set-cookie 标头实现的,此时无法修改 HTTP 头了,写 Cookie 会抛异常。所以只能在 OnResultExecuting 方法中写。
定义一个控制器。
public class DemoController : ControllerBase
{
[HttpGet("abc/test")]
public string Work() => "山重水复疑无路";
}
将自定义的结果过滤器添加为全局。
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(options =>
{
options.Filters.Add<SetCookieResultFilter>();
});
var app = builder.Build();
好,试试看。

Layui实现图片列表并且可以放大查看 - Core、陈 - 博客园
来源: Layui实现图片列表并且可以放大查看 – Core、陈 – 博客园
首先建一个DIV层
1 <div class="layui-row layui-col-space10"> 2 <div class="layui-col-md12"> 3 <div class="layui-card"> 4 <div class="layui-card-body"> 5 <fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px;"> 6 <legend>图片列表</legend> 7 </fieldset> 8 <div class="layui-row layui-col-space30" style="height: 300px; overflow:auto" id="LAY_Images"> 9 </div> 10 </div> 11 </div> 12 </div> 13 </div>
然后写一个数据请求的方法
1 //请求图像数据
2 $.ajax({
3 url: "接口路径",
4 data: { 'Id': 1 },
5 type: "post",
6 dataType: "json",
7 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
8 success: function (data) {
9 $("#LAY_Images").empty();
10 $.each(data.data, function (index, item) {
11 $("#LAY_Images").append(
12 "<div class='layui-col-md2 ew-datagrid-item'>" +
13 "<div class='project-list-item'>" +
14 "<img class='project-list-item-cover' src='" + item.imgname + "' onclick='previewImg(this)' />" +
15 "</div>" +
16 "</div>"
17 );
18 })
19 form.render($('#LAY_Images'));
20 },
21 error: function (data) {
22
23 }
24 });
这样就渲染好图片的列表了,如果不想要放大功能,去掉onclick事件就可以了,如果需要,加上下面的方法
1 //点击图片放大查看
2 function previewImg(obj) {
3 var img = new Image();
4 img.src = obj.src;
5 var height = img.height; //获取图片高度
6 var width = img.width; //获取图片宽度
7 if (height > 1000 || width > 800) {
8 height = height / 1.5;
9 width = width / 1.5;
10 }
11 var imgHtml = "<img src='" + obj.src + "' style='width: " + width + "px;height:" + height + "px'/>";
12 //弹出层
13 layer.open({
14 type: 1,
16 offset: 'auto',
17 area: [width + 'px', height + 'px'],
18 shadeClose: true,//点击外围关闭弹窗
19 scrollbar: true,//不现实滚动条
20 title: false, //不显示标题
21 content: imgHtml, //捕获的元素,注意:最好该指定的元素要存放在body最外层,否则可能被其它的相对元素所影响
22 cancel: function () {
23
24 }
25 });
26 }
这样基本就可以做出一个图片列表框出来了

原生js使用FileReader将文件转成base64_js filereader base64-CSDN博客
来源: 原生js使用FileReader将文件转成base64_js filereader base64-CSDN博客
一、FileReader介绍
FileReader 对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容。可以使用File对象指定要读取的文件。
File对象可以是来自用户在一个type类型为file的input元素上选择文件后返回的FileList对象。
<input type=”file” name=”” id=””>
注意: FileReader仅用于以安全的方式从用户(远程)系统读取文件内容,它不能用于从文件系统中直接使用路径名简单地读取文件。
二、FileReader的使用
FileReader是一个构造函数,需要new之后拿到FileReader的实例。
var reader = new FileReader();
1
实例的方法:
reader.abort():中止读取操作。
reader.readAsArrayBuffer():读取完成后,触发onload事件,result 属性中保存的是文件的 ArrayBuffer 数据对象。
reader.readAsDataURL():读取完成后,触发onload事件,result属性中保存的是Base64文件内容。
reader.readAsText():读取完成后,触发onload事件,result属性中保存的是字符串文件内容。
实例的事件:
reader.onabort:在读取操作被中断时触发。
reader.onerror:在读取操作发生错误时触发。
reader.onload:在读取操作完成时触发。
reader.onloadstart:在读取操作开始时触发。
reader.onloadend:在读取操作结束时(要么成功,要么失败)触发。
实例的属性:
reader.error:返回读取文件时的错误信息
reader.readyState:表示FileReader状态的数字。0:还没有加载;1:正在加载;2:加载完成
reader.result:文件的内容
三、使用范例
obtn.onclick = function(){
var reader = new FileReader();
reader.readAsDataURL(f.files[0]); // 解析成base64格式
reader.onload = function () {
console.log(this.result); // 解析后的数据,如下图
}
}
obtn.onclick = function(){
var reader = new FileReader();
reader.readAsArrayBuffer(f.files[0]);// 解析成ArrayBuffer格式
reader.onload = function () {
console.log(this.result); // 解析后的数据,如下图
}
}
obtn.onclick = function(){
var reader = new FileReader();
reader.readAsText(f.files[0]); // 解析成Text格式
reader.onload = function () {
console.log(this.result); // 解析后的数据,如下图
}
}
以上,如有错误,请留言交流…
————————————————
版权声明:本文为CSDN博主「杨树林er」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_41636483/article/details/112589158
【Windows系统】查看和关闭139、445端口的方法_关闭139端口_jin_study的博客-CSDN博客
来源: 【Windows系统】查看和关闭139、445端口的方法_关闭139端口_jin_study的博客-CSDN博客
文章目录
前言
一、 Windows查看139、445端口的方法
二、关闭445端口的方法
三、关闭139端口的方法
前言
“航天派”公众号上一期文章介绍了“麒麟操作系统查看和关闭139、445端口的方法”,今天介绍“Windows系统查看和关闭139、445端口的方法”。
前期,尝试使用一些软件关闭139、445端口,结果效果不理想;尝试使用网络上的一些操作方法,效果也不佳。今天介绍的关闭端口方法是经过多次实践验证过的,是真实有效的。需要提前说明,使用本文关闭139、445端口的方法,要在操作完成之后进行系统重启才能生效。
一、 Windows查看139、445端口的方法
按Windows+R键,弹出运行窗口,输入cmd回车,然后在命令提示符窗口输入如下命令,查看139、445端口是否存在:
netstat -na | findstr 139
netstat -na | findstr 445
1
2
如下图,可以看到该计算机TCP协议下均存在139、445端口,且处于LISTENING状态,接下来将封闭该计算机的139、445端口。
二、关闭445端口的方法
(1)在命令提升符号窗口接着输入regedit,将弹出注册表编辑器。
(2)在注册表编辑器找到HKEY_LOCAL_MACHINE—>SYSTEM—>ControlSet001—>services,如下图。
(3)在services中,找到NetBT—>Parameters,然后选中Parameters右键新建DWORD(32-位)值,如下图。
(4)将新建的DWORD命名为“SMBDericeEnabled”,并将其值修改为0(默认就是0,可以不用改),如下图。
(5)打开控制面板,选择管理工具—>服务—>Server—属性,如下图。
(6)在属性窗口将启动类型改为“禁用”,将服务状态选为“暂停”,然后点击应用、确定即可,如下图。
(7)完成以上操作之后,重启计算机,再次使用netstat -na | findstr 445查询,发现445端口已经关闭。当然可以等关闭完139端口之后,一起重启计算机,下面介绍关闭139端口的方法。
三、关闭139端口的方法
关闭139端口的方法,相比关闭445端口的方法,要简单很多。
(1)通过控制面板或者直接双击状态栏中网络连接图标,打开“网络和共享中心”,选择“更改适配器设置”—>本地连接—>属性,然后选择Internet协议版本4(TCP/IPv4)—>高级—>WINS—>禁用TCP/IP上的NetBIOS(S),然后点击确定即可,如下图。
(2)完成以上操作之后,重启计算机,然后在命令提示符窗口输入netstat -na | findstr 139、netstat -na | findstr 445查看139、445端口是否已关闭。如下图,经查询,139、445端口已经不存在,已经被关闭。
以上介绍了Windows系统查看和关闭139、445端口的方法,是经过多次实践验证过的,方法靠谱,值得分享。
关注“航天派”微信公众号,了解以下更好实用内容:
————————————————
版权声明:本文为CSDN博主「jin_study」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/jin_study/article/details/124198383
MVC将Base64 保存为图片 - 跑着的小强 - 博客园
来源: MVC将Base64 保存为图片 – 跑着的小强 – 博客园
前台传来Base64字符串。本来可以直接保存数据库返回给图片 但是这样对数据库负担太重 传输也费时间。一搬都是存个地址在数据库
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public ActionResult Injpg(string base64str ) { string data=base64str//要处理下字符串 ,之前的要截取掉 不然会报错 byte[] arr = Convert.FromBase64String(data); using (MemoryStream ms = new MemoryStream(arr)) { Bitmap bmp = new Bitmap(ms); string p = "/text.jpg"; var w = Server.MapPath(p); bmp.Save(w, System.Drawing.Imaging.ImageFormat.Jpeg); //bmp.Save(@"d:\"test.bmp", ImageFormat.Bmp); //bmp.Save(@"d:\"test.gif", ImageFormat.Gif); //bmp.Save(@"d:\"test.png", ImageFormat.Png); ms.Close(); return Content(p); }} |
解锁新技能 哈哈哈 不用后台压缩了,,,整理下代码 搭建微动态数据库表了
Mikel

