Net5 WorkService 继承 Quarzt 以及 Net5处理文件上传
Net5 版本以Core为底层非framework框架的windowservice 服务。
在VS里叫WorkService 可以以CMD方式运行也可以以Windowservice方式运行,部署简单。
Program.cs如下,是关键配置和启动项
using Microsoft.Extensions.Hosting; using Quartz; using WorkerService.Common; using WorkerService.Job; namespace WorkerService { public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args).UseWindowsService() .ConfigureServices((hostContext, services) => { #region 原生work Service //自定义调度 //services.AddHostedService<Worker>(); #endregion #region quartz 原始版本 //这个版本 trigger job Schedule 是唯一关联,不能一个组下多个任务 //services.AddQuartz(q => //{ // q.UseMicrosoftDependencyInjectionScopedJobFactory(); // // Create a "key" for the job // var jobKey = new JobKey("HelloTestJob"); // // Register the job with the DI container // q.AddJob<HelloTestJob>(opts => opts.WithIdentity(jobKey)); // // Create a trigger for the job // q.AddTrigger(opts => opts // .ForJob(jobKey) // link to the HelloWorldJob // .WithIdentity("HelloTestJob-trigger") // give the trigger a unique name // .WithCronSchedule("0/1 * * * * ?")); // run every 1 seconds //}); //services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true); #endregion #region quarzt 优化版本 //services.AddQuartz(q => //{ // q.UseMicrosoftDependencyInjectionScopedJobFactory(); // // Register the job, loading the schedule from configuration // q.AddJobAndTrigger<HelloTestJob>(hostContext.Configuration, "0/1 * * * * ?");//每秒运行一次 // q.AddJobAndTrigger<HelloTestJob2>(hostContext.Configuration, "0/1 * * * * ?"); //}); //services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true); #endregion #region 温湿度 SF6 红外图片上传 services.AddQuartz(q => { q.UseMicrosoftDependencyInjectionScopedJobFactory(); //每秒 0/1 * * * * ? 每小时 0 0 * * * ? // Register the job, loading the schedule from configuration q.AddJobAndTrigger<TemperatureJob>(hostContext.Configuration, "0 0 * * * ?"); q.AddJobAndTrigger<SF6Job>(hostContext.Configuration, "0 0 * * * ?"); q.AddJobAndTrigger<InfraredJob>(hostContext.Configuration, "0 0 * * * ?"); }); services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true); #endregion }); } }
原始的 Host.CreateDefaultBuilder(args) 需要增加 .UseWindowsService() 支持 对windowservice
quarzt 在 NET5的nuget 中叫 Quartz.Extensions.Hosting
services.AddHostedService<Worker>(); 是原始的windows定时任务版本
代码如下, 在 await Task.Delay(1000, stoppingToken); 设定定时启动的毫秒数就可以了
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using System; using System.IO; using System.Threading; using System.Threading.Tasks; namespace WorkerService.Job.Test { public class Worker : BackgroundService { private readonly ILogger<Worker> _logger; public Worker(ILogger<Worker> logger) { _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now); FileStream stream = new FileStream(@"d:\aa.txt", FileMode.Create);//fileMode指定是读取还是写入 StreamWriter writer = new StreamWriter(stream); writer.WriteLine("123456"+ DateTimeOffset.Now);//写入一行,写完后会自动换行 writer.Write("abc");//写完后不会换行 writer.WriteLine("ABC"); writer.Close();//释放内存 stream.Close();//释放内存 await Task.Delay(1000, stoppingToken); } } } }
quartz 原始版本(program.cs代码截图)
在目前这个quartz 3.3.3 版本中好像不能一个Key 下多个Job集成作业。所以每个job需要一个一个注册。推荐使用优化版本
quarzt 优化版本(program.cs代码截图)
对原始版本进行了封装。在每一次调用的时候会注册新的唯一实例。
以下是帮助类
using Microsoft.Extensions.Configuration; using Quartz; using System; namespace WorkerService.Common { public static class ServiceCollectionQuartzConfiguratorExtensions { public static void AddJobAndTrigger<T>( this IServiceCollectionQuartzConfigurator quartz, IConfiguration config, string cronSchedule) where T : IJob { // Use the name of the IJob as the appsettings.json key string jobName = typeof(T).Name; // Try and load the schedule from configuration var configKey = $"Quartz:{jobName}"; //var cronSchedule = config[configKey]; // Some minor validation if (string.IsNullOrEmpty(cronSchedule)) { throw new Exception($"No Quartz.NET Cron schedule found for job in configuration at {configKey}"); } // register the job as before var jobKey = new JobKey(jobName); quartz.AddJob<T>(opts => opts.WithIdentity(jobKey)); quartz.AddTrigger(opts => opts .ForJob(jobKey) .WithIdentity(jobName + "-trigger") .WithCronSchedule(cronSchedule)); // use the schedule from configuration } } }
以下是Job
using Microsoft.Extensions.Logging; using Quartz; using System; using System.IO; using System.Threading.Tasks; namespace WorkerService.Job.Test { [DisallowConcurrentExecution] public class HelloTestJob2 : IJob { private readonly ILogger<HelloTestJob2> _logger; public HelloTestJob2(ILogger<HelloTestJob2> logger) { _logger = logger; } public Task Execute(IJobExecutionContext context) { FileStream stream = new FileStream(@"d:\aa1.txt", FileMode.Create);//fileMode指定是读取还是写入 StreamWriter writer = new StreamWriter(stream); writer.WriteLine("123456aaa" + DateTimeOffset.Now);//写入一行,写完后会自动换行 writer.Write("abc");//写完后不会换行 writer.WriteLine("ABC"); writer.Close();//释放内存 stream.Close();//释放内存 return Task.CompletedTask; } } }
程序会根据Corn 设定的运行时间定期在 Task Execute(IJobExecutionContext context)方法内运行
然后就是蛮搞笑的,大伙都不用Net5 吗。写服务上传文件。遇到问题搜索NET5处理文件上传问题,居然都是空白的。 那我就只好自己写解决方案了。
客户端图片上传的HTTPHelper.cs部分代码如下
/// <summary> /// 上传文件 /// </summary> /// <param name="url">请求地址</param> /// <param name="path">文件路径(带文件名)</param> /// <returns></returns> public static string HttpPostFile(string url, string path) { // 设置参数 HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest; CookieContainer cookieContainer = new CookieContainer(); request.CookieContainer = cookieContainer; request.AllowAutoRedirect = true; request.Method = "POST";string boundary = DateTime.Now.Ticks.ToString("X"); // 随机分隔线 request.ContentType = "multipart/form-data;charset=utf-8;boundary=" + boundary; byte[] itemBoundaryBytes = Encoding.UTF8.GetBytes("\r\n--" + boundary + "\r\n"); byte[] endBoundaryBytes = Encoding.UTF8.GetBytes("\r\n--" + boundary + "--\r\n"); int pos = path.LastIndexOf("\\"); string fileName = path.Substring(pos + 1); //请求头部信息 StringBuilder sbHeader = new StringBuilder(string.Format("Content-Disposition:form-data;name=\"file\";filename=\"{0}\"\r\nContent-Type:application/octet-stream\r\n\r\n", fileName)); byte[] postHeaderBytes = Encoding.UTF8.GetBytes(sbHeader.ToString()); StringBuilder builder = new StringBuilder($"Content-Disposition:form-data;name=\"subPath\"\r\n\r\ntmswechat"); byte[] postHeaderBytestwo = Encoding.UTF8.GetBytes(builder.ToString()); FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read); byte[] bArr = new byte[fs.Length]; fs.Read(bArr, 0, bArr.Length); fs.Close(); Stream postStream = request.GetRequestStream(); postStream.Write(itemBoundaryBytes, 0, itemBoundaryBytes.Length); postStream.Write(postHeaderBytes, 0, postHeaderBytes.Length); postStream.Write(bArr, 0, bArr.Length); postStream.Write(itemBoundaryBytes, 0, itemBoundaryBytes.Length); postStream.Write(postHeaderBytestwo, 0, postHeaderBytestwo.Length); postStream.Write(endBoundaryBytes, 0, endBoundaryBytes.Length); postStream.Close(); //发送请求并获取相应回应数据 HttpWebResponse response = request.GetResponse() as HttpWebResponse; //直到request.GetResponse()程序才开始向目标网页发送Post请求 Stream instream = response.GetResponseStream(); StreamReader sr = new StreamReader(instream, Encoding.UTF8); //返回结果网页(html)代码 string content = sr.ReadToEnd(); return content; }
重点是服务端的接收,部分代码如下
try { var files = Request.Form.Files; if (files != null) { var file = files[0]; var location = System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase + $"Image\\" + file.FileName; if (!Directory.Exists(System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase + $"Image\\")) //判断上传文件夹是否存在,若不存在,则创建 { Directory.CreateDirectory(System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase + $"Image\\"); //创建文件夹 } using (var stream = new FileStream(location, FileMode.Create)) { await file.CopyToAsync(stream); result = 1; } } //using (var reader = new StreamReader(Request.Body))//从身体里读取 //{ // var body = await reader.ReadToEndAsync(); //} } catch (Exception e ) { throw; }
哪怕你用的是文件流上传,不是表单提交。但是你的文件依旧在Request.Form.Files 里!!!!
但你也可以通过Request.body 读到流
//using (var reader = new StreamReader(Request.Body))//从身体里读取 //{ // var body = await reader.ReadToEndAsync(); //}