日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

Asp.Net Core 快速邮件队列设计与实现

發(fā)布時間:2023/12/4 asp.net 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Asp.Net Core 快速邮件队列设计与实现 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

發(fā)送郵件幾乎是軟件系統(tǒng)中必不可少的功能,在Asp.Net Core 中我們可以使用MailKit發(fā)送郵件,MailKit發(fā)送郵件比較簡單,網(wǎng)上有許多可以參考的文章,但是應(yīng)該注意附件名長度,和附件名不能出現(xiàn)中文的問題,如果你遇到了這樣的問題可以參考我之前寫的這篇博客Asp.Net Core MailKit 完美附件(中文名、長文件名)。

在我們簡單搜索網(wǎng)絡(luò),并成功解決了附件的問題之后,我們已經(jīng)能夠發(fā)送郵件啦!不過另一個問題顯現(xiàn)出來——發(fā)送郵件太慢了,沒錯,在我使用QQ郵箱發(fā)送時,單封郵件發(fā)送大概要用1.5秒左右,用戶可能難以忍受請求發(fā)生1.5秒的延遲。

所以,我們必須解決這個問題,我們的解決辦法就是使用郵件隊列來發(fā)送郵件

設(shè)計郵件隊列

Ok, 第一步就是規(guī)劃我們的郵件隊列有什么

EmailOptions

我們得有一個郵件Options類,來存儲郵件相關(guān)的選項

/// <summary>
/// 郵件選項
/// </summary>
public class EmailOptions{ ?
?public bool DisableOAuth { get; set; } ?
? ?public string DisplayName { get; set; } ?
? ? ?public string Host { get; set; } // 郵件主機地址public string Password { get; set; } ?
? ? ??public int Port { get; set; } ?
? ? ???public string UserName { get; set; } ?
? ? ????public int SleepInterval { get; set; } = 3000;...

SleepInterval?是睡眠間隔,因為目前我們實現(xiàn)的隊列是進(jìn)程內(nèi)的獨立線程,發(fā)送器會循環(huán)讀取隊列,當(dāng)隊列是空的時候,我們應(yīng)該讓線程休息一會,不然無限循環(huán)會消耗大量CPU資源

然后我們還需要的就是 一個用于存儲郵件的隊列,或者叫隊列提供器,總之我們要將郵件存儲起來。以及一個發(fā)送器,發(fā)送器不斷的從隊列中讀取郵件并發(fā)送。還需要一個郵件寫入工具,想要發(fā)送郵件的代碼使用寫入工具將郵件轉(zhuǎn)儲到隊列中。

那么我們設(shè)計的郵件隊列事實上就有了三個部分:

  • 隊列存儲提供器(郵件的事實存儲)

  • 郵件發(fā)送機 (不斷讀取隊列中的郵件,并發(fā)送)

  • 郵件服務(wù) (想法送郵件時,調(diào)用郵件服務(wù),郵件服務(wù)會將郵件寫入隊列)

隊列存儲提供器設(shè)計

那么我們設(shè)計的郵件隊列提供器接口如下:

public interface IMailQueueProvider{ ?
?void Enqueue(MailBox mailBox); ?
? ?bool TryDequeue(out MailBox mailBox); ?
? ??int Count { get; } ?
? ?? ?bool IsEmpty { get; }...

四個方法,入隊、出隊、隊列剩余郵件數(shù)量、隊列是否是空,我們對隊列的基本需求就是這樣。

MailBox是對郵件的封裝,并不復(fù)雜,稍后會介紹到

郵件服務(wù)設(shè)計

public interface IMailQueueService{ ?
? ? ?void Enqueue(MailBox box);

對于想要發(fā)送郵件的組件或者代碼部分來講,只需要將郵件入隊,這就足夠了

郵件發(fā)送機(兼郵件隊列管理器)設(shè)計

public interface IMailQueueManager{ ?
?void Run(); ?
? ?void Stop(); ?
? ?bool IsRunning { get; } ?
? ??int Count { get; } ? ?

啟動隊列,停止隊列,隊列運行中狀態(tài),郵件計數(shù)

現(xiàn)在,三個主要部分就設(shè)計好了,我們先看下MailBox,接下來就去實現(xiàn)這三個接口

MailBox

MailBox 如下:

public class MailBox{
? ?public IEnumerable<IAttachment> Attachments { get; set; }
? ?? ?public string Body { get; set; } ?
? ?? ? ?public IEnumerable<string> Cc { get; set; }
? ?? ? ? ? ?public bool IsHtml { get; set; }
? ?? ? ? ? ?? ?public string Subject { get; set; }
? ?? ? ? ? ?? ?? ?public IEnumerable<string> To { get; set; }...

這里面沒什么特殊的,大家一看便能理解,除了IEnumerable<IAttachment> Attachments { get; set; }。

附件的處理

在發(fā)送郵件中最復(fù)雜的就是附件了,因為附件體積大,往往還涉及非托管資源(例如:文件),所以附件處理一定要小心,避免留下漏洞和bug。

在MailKit中附件實際上是流Stream,例如下面的代碼:

attachment = new MimePart(contentType) {Content = new MimeContent(fs),ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),ContentTransferEncoding = ContentEncoding.Base64, };

其中new MimeContent(fs)是創(chuàng)建的Content,fs是Stream,MimeContent的構(gòu)造函數(shù)如下:

public MimeContent(Stream stream, ContentEncoding encoding = ContentEncoding.Default)

所以我們的設(shè)計的附件是基于Stream的。

一般情況附件是磁盤上的文件,或者內(nèi)存流MemoryStream或者 byte[]數(shù)據(jù)。附件需要實際的文件的流Stream和一個附件名,所以附件接口設(shè)計如下:

public interface IAttachment : IDisposable{ ? ?Stream GetFileStream(); ? ?string GetName();

那么我們默認(rèn)實現(xiàn)了兩中附件類型?物理文件附件內(nèi)存文件附件,byte[]數(shù)據(jù)可以輕松的轉(zhuǎn)換成 內(nèi)存流,所以沒有寫這種

MemoryStreamAttechment

public class MemoryStreamAttechment : IAttachment{ ?
?private readonly MemoryStream _stream; ?
??private readonly string _fileName; ?
???public MemoryStreamAttechment(MemoryStream stream, string fileName) ? ?{_stream = stream;_fileName = fileName;} ?
???
???public void Dispose() ? ? ? ?=> _stream.Dispose();
???public Stream GetFileStream() ? ? ? ?=> _stream; ?
???public string GetName() ? ? ? ?=> _fileName;

內(nèi)存流附件實現(xiàn)要求在創(chuàng)建時傳遞一個 MemoryStream和附件名稱,比較簡單

物理文件附件

public class PhysicalFileAttachment : IAttachment{ ?
?public PhysicalFileAttachment(string absolutePath) ? ?{ ?
?
?? ? ?if (!File.Exists(absolutePath)){ ? ? ? ? ?
?? ? ??throw new FileNotFoundException("文件未找到", absolutePath);}AbsolutePath = absolutePath;} ? ?
?? ? ??private FileStream _stream; ?
?? ? ?? ?public string AbsolutePath { get; } ?
?? ? ?? ? ?public void Dispose() ? ?{_stream.Dispose();} ?
?? ? ? ?public Stream GetFileStream() ? ?{ ? ?
?? ? ? ? ? ?if (_stream == null){_stream = new FileStream(AbsolutePath, FileMode.Open);} ? ? ? ?return _stream;} ? ?public string GetName() ? ?{ ? ? ?
?? ? ? ? ? ? ?return System.IO.Path.GetFileName(AbsolutePath);... ? ?

這里,我們要注意的是創(chuàng)建FileStream的時機,是在請求GetFileStream方法時,而不是構(gòu)造函數(shù)中,因為創(chuàng)建FileStreamFileStream會占用文件,如果我們發(fā)兩封郵件使用了同一個附件,那么會拋出異常。而寫在GetFileStream方法中相對比較安全(除非發(fā)送器是并行的)

實現(xiàn)郵件隊列

在我們這篇文章中,我們實現(xiàn)的隊列提供器是基于內(nèi)存的,日后呢我們還可以實現(xiàn)其它的基于其它存儲模式的,比如數(shù)據(jù)庫,外部持久性隊列等等,另外基于內(nèi)存的實現(xiàn)不是持久的,一旦程序崩潰。未發(fā)出的郵件就會boom然后消失 XD...

郵件隊列提供器IMailQueueProvider實現(xiàn)

代碼如下:

public class MailQueueProvider : IMailQueueProvider{ ?
?private static readonly ConcurrentQueue<MailBox> _mailQueue = new ConcurrentQueue<MailBox>(); ? ?public int Count => _mailQueue.Count; ? ?public bool IsEmpty => _mailQueue.IsEmpty; ? ?public void Enqueue(MailBox mailBox) ? ?{_mailQueue.Enqueue(mailBox);} ? ?public bool TryDequeue(out MailBox mailBox) ? ?{ ? ? ? ?return _mailQueue.TryDequeue(out mailBox);}

本文的實現(xiàn)是一個 ConcurrentQueue

郵件服務(wù)IMailQueueService實現(xiàn)

代碼如下:

public class MailQueueService : IMailQueueService{ ? ?private readonly IMailQueueProvider _provider; ? ?/// <summary>/// 初始化實例/// </summary>/// <param name="provider"></param>public MailQueueService(IMailQueueProvider provider) ? ?{_provider = provider;} ? ?/// <summary>/// 入隊/// </summary>/// <param name="box"></param>public void Enqueue(MailBox box) ? ?{_provider.Enqueue(box);} ? ?

這里,我們的服務(wù)依賴于IMailQueueProvider,使用了其入隊功能

郵件發(fā)送機IMailQueueManager實現(xiàn)

這個相對比較復(fù)雜,我們先看下完整的類,再逐步解釋:

public class MailQueueManager : IMailQueueManager{ ? ?private readonly SmtpClient _client; ? ?private readonly IMailQueueProvider _provider; ? ?private readonly ILogger<MailQueueManager> _logger; ? ?private readonly EmailOptions _options; ? ?private bool _isRunning = false; ? ?private bool _tryStop = false; ? ?private Thread _thread; ? ?/// <summary>/// 初始化實例/// </summary>/// <param name="provider"></param>/// <param name="options"></param>/// <param name="logger"></param>public MailQueueManager(IMailQueueProvider provider, IOptions<EmailOptions> options, ILogger<MailQueueManager> logger) ? ?{_options = options.Value;_client = new SmtpClient{ ? ? ? ? ? ?// For demo-purposes, accept all SSL certificates (in case the server supports STARTTLS)ServerCertificateValidationCallback = (s, c, h, e) => true}; ? ? ? ?// Note: since we don't have an OAuth2 token, disable// the XOAUTH2 authentication mechanism.if (_options.DisableOAuth){_client.AuthenticationMechanisms.Remove("XOAUTH2");}_provider = provider;_logger = logger;} ? ?/// <summary>/// 正在運行/// </summary>public bool IsRunning => _isRunning; ? ?/// <summary>/// 計數(shù)/// </summary>public int Count => _provider.Count; ? ?/// <summary>/// 啟動隊列/// </summary>public void Run() ? ?{ ? ? ? ?if (_isRunning || (_thread != null && _thread.IsAlive)){_logger.LogWarning("已經(jīng)運行,又被啟動了,新線程啟動已經(jīng)取消"); ? ? ? ? ? ?return;}_isRunning = true;_thread = new Thread(StartSendMail){Name = "PmpEmailQueue",IsBackground = true,};_logger.LogInformation("線程即將啟動");_thread.Start();_logger.LogInformation("線程已經(jīng)啟動,線程Id是:{0}", _thread.ManagedThreadId);} ? ?/// <summary>/// 停止隊列/// </summary>public void Stop() ? ?{ ? ? ? ?if (_tryStop){ ? ? ? ? ? ?return;}_tryStop = true;} ? ?private void StartSendMail() ? ?{ ? ? ? ?var sw = new Stopwatch(); ? ? ? ?try{ ? ? ? ? ? ?while (true){ ? ? ? ? ? ? ? ?if (_tryStop){ ? ? ? ? ? ? ? ? ? ?break;} ? ? ? ? ? ? ? ?if (_provider.IsEmpty){_logger.LogTrace("隊列是空,開始睡眠");Thread.Sleep(_options.SleepInterval); ? ? ? ? ? ? ? ? ? ?continue;} ? ? ? ? ? ? ? ?if (_provider.TryDequeue(out MailBox box)){_logger.LogInformation("開始發(fā)送郵件 標(biāo)題:{0},收件人 {1}", box.Subject, box.To.First());sw.Restart();SendMail(box);sw.Stop();_logger.LogInformation("發(fā)送郵件結(jié)束標(biāo)題:{0},收件人 {1},耗時{2}", box.Subject, box.To.First(), sw.Elapsed.TotalSeconds);}}} ? ? ? ?catch (Exception ex){_logger.LogError(ex, "循環(huán)中出錯,線程即將結(jié)束");_isRunning = false;}_logger.LogInformation("郵件發(fā)送線程即將停止,人為跳出循環(huán),沒有異常發(fā)生");_tryStop = false;_isRunning = false;} ? ?private void SendMail(MailBox box) ? ?{ ? ? ? ?if (box == null){ ? ? ? ? ? ?throw new ArgumentNullException(nameof(box));} ? ? ? ?try{MimeMessage message = ConvertToMimeMessage(box);SendMail(message);} ? ? ? ?catch (Exception exception){_logger.LogError(exception, "發(fā)送郵件發(fā)生異常主題:{0},收件人:{1}", box.Subject, box.To.First());} ? ? ? ?finally{ ? ? ? ? ? ?if (box.Attachments != null && box.Attachments.Any()){ ? ? ? ? ? ? ? ?foreach (var item in box.Attachments){item.Dispose();}}}} ? ?private MimeMessage ConvertToMimeMessage(MailBox box) ? ?{ ? ? ? ?var message = new MimeMessage(); ? ? ? ?var from = InternetAddress.Parse(_options.UserName); ? ? ? ?from.Name = _options.DisplayName;message.From.Add(from); ? ? ? ?if (!box.To.Any()){ ? ? ? ? ? ?throw new ArgumentNullException("to必須含有值");}message.To.AddRange(box.To.Convert()); ? ? ? ?if (box.Cc != null && box.Cc.Any()){message.Cc.AddRange(box.Cc.Convert());}message.Subject = box.Subject; ? ? ? ?var builder = new BodyBuilder(); ? ? ? ?if (box.IsHtml){builder.HtmlBody = box.Body;} ? ? ? ?else{builder.TextBody = box.Body;} ? ? ? ?if (box.Attachments != null && box.Attachments.Any()){ ? ? ? ? ? ?foreach (var item in GetAttechments(box.Attachments)) ? ? ? ? ? ?{builder.Attachments.Add(item);}}message.Body = builder.ToMessageBody(); ? ? ? ?return message;} ? ?private void SendMail(MimeMessage message) ? ?{ ? ? ? ?if (message == null){ ? ? ? ? ? ?throw new ArgumentNullException(nameof(message));} ? ? ? ?try{_client.Connect(_options.Host, _options.Port, false); ? ? ? ? ? ?// Note: only needed if the SMTP server requires authenticationif (!_client.IsAuthenticated){_client.Authenticate(_options.UserName, _options.Password);}_client.Send(message);} ? ? ? ?finally{_client.Disconnect(false);}} ? ?private AttachmentCollection GetAttechments(IEnumerable<IAttachment> attachments) ? ?{ ? ? ? ?if (attachments == null){ ? ? ? ? ? ?throw new ArgumentNullException(nameof(attachments));}AttachmentCollection collection = new AttachmentCollection();List<Stream> list = new List<Stream>(attachments.Count()); ? ? ? ?foreach (var item in attachments){ ? ? ? ? ? ?var fileName = item.GetName(); ? ? ? ? ? ?var fileType = MimeTypes.GetMimeType(fileName); ? ? ? ? ? ?var contentTypeArr = fileType.Split('/'); ? ? ? ? ? ?var contentType = new ContentType(contentTypeArr[0], contentTypeArr[1]);MimePart attachment = null;Stream fs = null; ? ? ? ? ? ?try{fs = item.GetFileStream();list.Add(fs);} ? ? ? ? ? ?catch (Exception ex){_logger.LogError(ex, "讀取文件流發(fā)生異常");fs?.Dispose(); ? ? ? ? ? ? ? ?continue;}attachment = new MimePart(contentType){Content = new MimeContent(fs),ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),ContentTransferEncoding = ContentEncoding.Base64,}; ? ? ? ? ? ?var charset = "UTF-8";attachment.ContentType.Parameters.Add(charset, "name", fileName);attachment.ContentDisposition.Parameters.Add(charset, "filename", fileName); ? ? ? ? ? ?foreach (var param in attachment.ContentDisposition.Parameters){param.EncodingMethod = ParameterEncodingMethod.Rfc2047;} ? ? ? ? ? ?foreach (var param in attachment.ContentType.Parameters){param.EncodingMethod = ParameterEncodingMethod.Rfc2047;}collection.Add(attachment);} ? ? ? ?return collection;} }

在構(gòu)造函數(shù)中請求了另外三個服務(wù),并且初始化了SmtpClient(這是MailKit中的)

? ?public MailQueueManager(IMailQueueProvider provider, IOptions<EmailOptions> options, ILogger<MailQueueManager> logger){_options = options.Value;_client = new SmtpClient{ ? ? ? ? ? ?// For demo-purposes, accept all SSL certificates (in case the server supports STARTTLS)ServerCertificateValidationCallback = (s, c, h, e) => true}; ? ? ? ?// Note: since we don't have an OAuth2 token, disable// the XOAUTH2 authentication mechanism.if (_options.DisableOAuth){_client.AuthenticationMechanisms.Remove("XOAUTH2");}_provider = provider;_logger = logger;}

啟動隊列時創(chuàng)建了新的線程,并且將線程句柄保存起來:

? ?public void Run() ? ?{ ? ? ? ?if (_isRunning || (_thread != null && _thread.IsAlive)){_logger.LogWarning("已經(jīng)運行,又被啟動了,新線程啟動已經(jīng)取消"); ? ? ? ? ? ?return;}_isRunning = true;_thread = new Thread(StartSendMail){Name = "PmpEmailQueue",IsBackground = true,};_logger.LogInformation("線程即將啟動");_thread.Start();_logger.LogInformation("線程已經(jīng)啟動,線程Id是:{0}", _thread.ManagedThreadId);}

線程啟動時運行了方法StartSendMail:

? ?private void StartSendMail() ? ?{ ? ? ? ?var sw = new Stopwatch(); ? ? ? ?try{ ? ? ? ? ? ?while (true){ ? ? ? ? ? ? ? ?if (_tryStop){ ? ? ? ? ? ? ? ? ? ?break;} ? ? ? ? ? ? ? ?if (_provider.IsEmpty){_logger.LogTrace("隊列是空,開始睡眠");Thread.Sleep(_options.SleepInterval); ? ? ? ? ? ? ? ? ? ?continue;} ? ? ? ? ? ? ? ?if (_provider.TryDequeue(out MailBox box)){_logger.LogInformation("開始發(fā)送郵件 標(biāo)題:{0},收件人 {1}", box.Subject, box.To.First());sw.Restart();SendMail(box);sw.Stop();_logger.LogInformation("發(fā)送郵件結(jié)束標(biāo)題:{0},收件人 {1},耗時{2}", box.Subject, box.To.First(), sw.Elapsed.TotalSeconds);}}} ? ? ? ?catch (Exception ex){_logger.LogError(ex, "循環(huán)中出錯,線程即將結(jié)束");_isRunning = false;}_logger.LogInformation("郵件發(fā)送線程即將停止,人為跳出循環(huán),沒有異常發(fā)生");_tryStop = false;_isRunning = false;} ? ?

這個方法不斷的從隊列讀取郵件并發(fā)送,當(dāng) 遇到異常,或者_(dá)tryStop為true時跳出循環(huán),此時線程結(jié)束,注意我們會讓線程睡眠,在適當(dāng)?shù)臅r候。

接下來就是方法SendMail了:

? ?private void SendMail(MailBox box) ? ?{ ? ? ? ?if (box == null){ ? ? ? ? ? ?throw new ArgumentNullException(nameof(box));} ? ? ? ?try{MimeMessage message = ConvertToMimeMessage(box);SendMail(message);} ? ? ? ?catch (Exception exception){_logger.LogError(exception, "發(fā)送郵件發(fā)生異常主題:{0},收件人:{1}", box.Subject, box.To.First());} ? ? ? ?finally{ ? ? ? ? ? ?if (box.Attachments != null && box.Attachments.Any()){ ? ? ? ? ? ? ? ?foreach (var item in box.Attachments){item.Dispose();... ? ? ? ? ? ? ? ?

這里有一個特別要注意的就是在發(fā)送之后釋放附件(非托管資源):

foreach (var item in box.Attachments) {item.Dispose();...

發(fā)送郵件的核心代碼只有兩行:

MimeMessage message = ConvertToMimeMessage(box);SendMail(message);

第一行將mailbox轉(zhuǎn)換成 MailKit使用的MimeMessage實體,第二步切實的發(fā)送郵件

為什么,我們的接口中沒有直接使用MimeMessage而是使用MailBox?

因為MimeMessage比較繁雜,而且附件的問題不易處理,所以我們設(shè)計接口時單獨封裝MailBox簡化了編程接口

轉(zhuǎn)換一共兩步,1是主體轉(zhuǎn)換,比較簡單。二是附件的處理這里涉及到附件名中文編碼的問題。

? ?private MimeMessage ConvertToMimeMessage(MailBox box) ? ?{ ? ? ? ?var message = new MimeMessage(); ? ? ? ?var from = InternetAddress.Parse(_options.UserName); ? ? ? ?from.Name = _options.DisplayName;message.From.Add(from); ? ? ? ?if (!box.To.Any()){ ? ? ? ? ? ?throw new ArgumentNullException("to必須含有值");}message.To.AddRange(box.To.Convert()); ? ? ? ?if (box.Cc != null && box.Cc.Any()){message.Cc.AddRange(box.Cc.Convert());}message.Subject = box.Subject; ? ? ? ?var builder = new BodyBuilder(); ? ? ? ?if (box.IsHtml){builder.HtmlBody = box.Body;} ? ? ? ?else{builder.TextBody = box.Body;} ? ? ? ?if (box.Attachments != null && box.Attachments.Any()){ ? ? ? ? ? ?foreach (var item in GetAttechments(box.Attachments)) ? ? ? ? ? ?{builder.Attachments.Add(item);}}message.Body = builder.ToMessageBody(); ? ? ? ?return message;} ? ?private AttachmentCollection GetAttechments(IEnumerable<IAttachment> attachments) ? ?{ ? ? ? ?if (attachments == null){ ? ? ? ? ? ?throw new ArgumentNullException(nameof(attachments));}AttachmentCollection collection = new AttachmentCollection();List<Stream> list = new List<Stream>(attachments.Count()); ? ? ? ?foreach (var item in attachments){ ? ? ? ? ? ?var fileName = item.GetName(); ? ? ? ? ? ?var fileType = MimeTypes.GetMimeType(fileName); ? ? ? ? ? ?var contentTypeArr = fileType.Split('/'); ? ? ? ? ? ?var contentType = new ContentType(contentTypeArr[0], contentTypeArr[1]);MimePart attachment = null;Stream fs = null; ? ? ? ? ? ?try{fs = item.GetFileStream();list.Add(fs);} ? ? ? ? ? ?catch (Exception ex){_logger.LogError(ex, "讀取文件流發(fā)生異常");fs?.Dispose(); ? ? ? ? ? ? ? ?continue;}attachment = new MimePart(contentType){Content = new MimeContent(fs),ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),ContentTransferEncoding = ContentEncoding.Base64,}; ? ? ? ? ? ?var charset = "UTF-8";attachment.ContentType.Parameters.Add(charset, "name", fileName);attachment.ContentDisposition.Parameters.Add(charset, "filename", fileName); ? ? ? ? ? ?foreach (var param in attachment.ContentDisposition.Parameters){param.EncodingMethod = ParameterEncodingMethod.Rfc2047;} ? ? ? ? ? ?foreach (var param in attachment.ContentType.Parameters){param.EncodingMethod = ParameterEncodingMethod.Rfc2047;}collection.Add(attachment);} ? ? ? ?return collection;}

在轉(zhuǎn)化附件時下面的代碼用來處理附件名編碼問題:

var charset = "UTF-8"; attachment.ContentType.Parameters.Add(charset, "name", fileName); attachment.ContentDisposition.Parameters.Add(charset, "filename", fileName);foreach (var param in attachment.ContentDisposition.Parameters) {param.EncodingMethod = ParameterEncodingMethod.Rfc2047; }foreach (var param in attachment.ContentType.Parameters) {param.EncodingMethod = ParameterEncodingMethod.Rfc2047; }

到這了我們的郵件隊列就基本完成了,接下來就是在程序啟動后,啟動隊列,找到 Program.cs文件,并稍作改寫如下:

var host = BuildWebHost(args);var provider = host.Services; provider.GetRequiredService<IMailQueueManager>().Run(); host.Run();

這里在host.Run()主機啟動之前,我們獲取了IMailQueueManager并啟動隊列(別忘了注冊服務(wù))。

運行程序我們會看到控制臺每隔3秒就會打出日志:

info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0]User profile is available. Using 'C:\Users\Administrator\AppData\Local\ASP.NET\DataProtection-Keys' as key repository and Windows DPAPI to encrypt keys at rest.info: MailQueueManager[0]線程即將啟動info: MailQueueManager[0]線程已經(jīng)啟動,線程Id是:9trce: MailQueueManager[0]隊列是空,開始睡眠 Hosting environment: Development Content root path: D:\publish Now listening on: http://[::]:5000Application started. Press Ctrl+C to shut down.trce: MailQueueManager[0]隊列是空,開始睡眠trce: MailQueueManager[0]隊列是空,開始睡眠

到此,我們的郵件隊列就完成了! :D

原文地址http://www.cnblogs.com/rocketRobin/p/9294845.html

.NET社區(qū)新聞,深度好文,歡迎訪問公眾號文章匯總 http://www.csharpkit.com

總結(jié)

以上是生活随笔為你收集整理的Asp.Net Core 快速邮件队列设计与实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。