在前面的一些文章中,有介紹過DotNet內(nèi)置SMTP類的郵件發(fā)送功能,附件、嵌入圖片的模式都有介紹,本文繼續(xù)介紹Lumisoft.NET這個非常優(yōu)秀的開源組件,用該組件來設(shè)計開發(fā)郵件工具,將變得更加方便,功能更加強(qiáng)大。網(wǎng)上很多文章基本介紹如何使用該組件來收取郵件較多,較少介紹使用該組件做郵件發(fā)送功能的。本文主要探尋使用該組件實現(xiàn)郵件的發(fā)送功能,郵件發(fā)送有兩種方式,一種是不用發(fā)件人即可發(fā)送郵件,一種是使用發(fā)件人賬戶密碼和SMTP服務(wù)器來實現(xiàn)郵件發(fā)送的,本文分別對這兩種方式進(jìn)行介紹。
組件下載地址:http://www.lumisoft.ee/lswww/download/downloads/
組件論壇地址:http://www.lumisoft.ee/Forum/default.aspx?g=forum
秉承一貫的做法,先貼出相關(guān)的實現(xiàn)圖形,感官認(rèn)識下,在進(jìn)入詳細(xì)的介紹說明,以求達(dá)到最好的理解深度。
1、 首先是發(fā)件人的設(shè)置,可以從文本文件的導(dǎo)出,以及新建等操作,以方便用戶操作。
2、 內(nèi)容也支持導(dǎo)入導(dǎo)出,并且保存到數(shù)據(jù)庫,方便進(jìn)行記錄及操作等,另外可以對內(nèi)容進(jìn)行隨機(jī)混淆,混淆的內(nèi)容在HTML郵件中式隱藏的,方便糊弄一下服務(wù)器的識別。
3、 郵件發(fā)送可以選擇兩種方式,下面將分別介紹這兩種方式的實現(xiàn),一種采用該控件封裝非常好的郵件直投技術(shù),不需要SMTP賬號發(fā)送的;一種是普通的SMTP發(fā)送方式。當(dāng)然我們還可以設(shè)置更多的參數(shù),例如郵件尾部信息、HTML內(nèi)容提示、 以及一些發(fā)送期間自動撥號的設(shè)置操作等。
4、 郵件直投技術(shù),通過模擬賬戶來附加用戶的郵件地址(或者可以成為偽裝)。其中我填寫了一些常用的SMTP服務(wù)器的域名,方便在其中構(gòu)造合乎要求的郵件格式,還可以設(shè)置郵件回執(zhí)通知,如下圖所示。
5、 如果是采用普通發(fā)送方式,那么就需要制定用戶的賬號密碼等信息,發(fā)送的時候,自動從啟動獲取發(fā)件人信息進(jìn)行批量發(fā)送操作。
6、 最后體驗一下少量郵件的發(fā)送效果,發(fā)送采用多線程發(fā)送,多線程采用比較有名的SmartThreadPool組件,并且發(fā)送過程總詳細(xì)記錄其中的日志,供參考。
介紹完畢相關(guān)的功能效果圖,下面我們來分析下主要功能實現(xiàn)的代碼:
復(fù)制代碼 代碼如下:
private TimerHelper timer = null;
private void btnSend_Click(object sender, EventArgs e)
{
//重置計數(shù)變量
failedItems = 0;
successItems = 0;
workItemsCompleted = 0;
workItemsGenerated = 0;
Portal.gc.FailedCount = 0;//重置失敗次數(shù)
STPStartInfo stpStartInfo = new STPStartInfo();
stpStartInfo.IdleTimeout = 10;
stpStartInfo.MaxWorkerThreads = 100;
stpStartInfo.MinWorkerThreads = 0;
//stpStartInfo.StartSuspended = true;
_smartThreadPool = new SmartThreadPool(stpStartInfo);
_workItemsGroup = _smartThreadPool;
workItemsProducerThread = new Thread(new ThreadStart(this.WorkItemsProducer));
workItemsProducerThread.IsBackground = true;
workItemsProducerThread.Start();
RefreshStatusCount();
int intervalRedial = SystemConfig.Default.IntervalRedial * 1000 * 60;
if (intervalRedial > 0)
{
if (timer != null)
{
timer.Stop();
timer.Dispose();
}
timer = new TimerHelper(intervalRedial,false);
timer.Execute += new TimerHelper.TimerExecution(timer_Execute);
timer.Start();
}
}
private static object locker = new object();
private void timer_Execute()
{
if (Monitor.TryEnter(locker))
{
string message = string.Format("在時間 {0} 時刻執(zhí)行了一次重?fù)芴柌僮鳎?, DateTime.Now);
ShowSendStatus(message);
string RasName = SystemConfig.Default.RasName;
if (!string.IsNullOrEmpty(RasName))
{
message = string.Format("正在準(zhǔn)備重新?lián)芴枺▄0})", RasName);
ShowSendStatus(message);
Portal.gc.ReConnect(RasName);
Portal.gc.FailedCount = 0;//重新歸零
}
Monitor.Exit(locker);
}
else
{
Monitor.Enter(locker);
Monitor.Exit(locker);
}
}
上面是主要的任務(wù)生成操作以及相關(guān)的撥號操作,其中任務(wù)詳細(xì)的生成代碼如下所示。
private void WorkItemsProducer()
復(fù)制代碼 代碼如下:
{
CallCtrlWithThreadSafetyEx.SetText(this.txtSendDetail, "");
EnableControl(false, true, true);
string message = string.Format("任務(wù)開始");
RecordMessage(message);
#region 生成任務(wù)
IWorkItemsGroup workItemsGroup = _workItemsGroup;
if (null == workItemsGroup)
{
return;
}
Liststring> addressList = GetAddressList();
ListMyMailInfo> mailInfoList = GetMailInfo();
for (int i = 0; i addressList.Count; i++)
{
try
{
SendJobInfo jobInfo = new SendJobInfo();
jobInfo.domainList = mailDomainList;
jobInfo.mailTo = addressList[i];
jobInfo.mailInfo = GetOneMail(mailInfoList, i);
jobInfo.ShowSendStatus = ShowSendStatus;
jobInfo.currentDomain = (i % mailDomainList.Count);//設(shè)置一個標(biāo)志,默認(rèn)那個賬戶開始發(fā)送
jobInfo.UseDirectSendType = SystemConfig.Default.EmailDirectSend;
//如果用戶未指定發(fā)送賬號,那么采用默認(rèn)的顯示名稱
//如果為空,發(fā)送的時候,會自動采用郵件地址作為顯示名稱
if (string.IsNullOrEmpty(SystemConfig.Default.UserEmailFrom))
{
jobInfo.mailFromDisplay = SystemConfig.Default.DefaultFromDisplayName;
}
workItemCallback = new WorkItemCallback(this.DoWork);
workItemsGroup.QueueWorkItem(workItemCallback, jobInfo);
Thread.Sleep(100);
}
catch (ObjectDisposedException ex)
{
LogTextHelper.WriteLine(ex.ToString());
continue;
}
Interlocked.Increment(ref workItemsGenerated);
}
#endregion
RefreshStatusCount();
message = string.Format("共有 {0} 個任務(wù),還剩下 {1} 個",
workItemsGenerated, workItemsGenerated - workItemsCompleted);
CallCtrlWithThreadSafetyEx.SetText(this, message);
RecordMessage(message);
try
{
//workItemsGroup.Start();
workItemsGroup.WaitForIdle();
_smartThreadPool.Shutdown();
}
catch (Exception ex)
{
LogTextHelper.WriteLine(ex.ToString());
}
UpdateFinishStatus();
}
由于采用了多線程來處理,所以停止發(fā)送的時候,需要把相關(guān)的線程對象進(jìn)行釋放,如下代碼所示。
復(fù)制代碼 代碼如下:
private void btnStop_Click(object sender, EventArgs e)
{
try
{
_smartThreadPool.Shutdown();
_smartThreadPool.Dispose();
_smartThreadPool = null;
if (timer != null)
{
timer.Stop();
timer.Dispose();
}
}
catch (Exception ex)
{
LogTextHelper.WriteLine(ex.ToString());
}
UpdateFinishStatus();
}
其中具體的郵件發(fā)送功能封裝在SendJobInfo中,通過判斷不同的類型,進(jìn)行不同的發(fā)送操作。
其中最為關(guān)鍵的發(fā)送代碼,就是如何利用LumiSoft.NET組件來構(gòu)造相應(yīng)的郵件對象,下面先先介紹下郵件直投的發(fā)送方式,由于該組件封裝比較好,直投發(fā)送方式很簡單:
復(fù)制代碼 代碼如下:
Mail_Message message = Create_PlainText_Html_Attachment_Image(mailTo, mailFrom, mailFromDisplay);
SMTP_Client.QuickSend(message);
其中Create_PlainText_Html_Attachment_Image的封裝函數(shù)詳細(xì)內(nèi)容如下所示:
復(fù)制代碼 代碼如下:
代碼
private Mail_Message Create_PlainText_Html_Attachment_Image(string mailTo, string mailFrom, string mailFromDisplay)
{
Mail_Message msg = new Mail_Message();
msg.MimeVersion = "1.0";
msg.MessageID = MIME_Utils.CreateMessageID();
msg.Date = DateTime.Now;
msg.From = new Mail_t_MailboxList();
msg.From.Add(new Mail_t_Mailbox(mailFromDisplay, mailFrom));
msg.To = new Mail_t_AddressList();
msg.To.Add(new Mail_t_Mailbox(mailTo, mailTo));
msg.Subject = mailInfo.Title;
//設(shè)置回執(zhí)通知
string notifyEmail = SystemConfig.Default.DispositionNotificationTo;
if (!string.IsNullOrEmpty(notifyEmail) ValidateUtil.IsEmail(notifyEmail))
{
msg.DispositionNotificationTo = new Mail_t_Mailbox(notifyEmail, notifyEmail);
}
#region MyRegion
//--- multipart/mixed -----------------------------------
MIME_h_ContentType contentType_multipartMixed = new MIME_h_ContentType(MIME_MediaTypes.Multipart.mixed);
contentType_multipartMixed.Param_Boundary = Guid.NewGuid().ToString().Replace('-', '.');
MIME_b_MultipartMixed multipartMixed = new MIME_b_MultipartMixed(contentType_multipartMixed);
msg.Body = multipartMixed;
//--- multipart/alternative -----------------------------
MIME_Entity entity_multipartAlternative = new MIME_Entity();
MIME_h_ContentType contentType_multipartAlternative = new MIME_h_ContentType(MIME_MediaTypes.Multipart.alternative);
contentType_multipartAlternative.Param_Boundary = Guid.NewGuid().ToString().Replace('-', '.');
MIME_b_MultipartAlternative multipartAlternative = new MIME_b_MultipartAlternative(contentType_multipartAlternative);
entity_multipartAlternative.Body = multipartAlternative;
multipartMixed.BodyParts.Add(entity_multipartAlternative);
//--- text/plain ----------------------------------------
MIME_Entity entity_text_plain = new MIME_Entity();
MIME_b_Text text_plain = new MIME_b_Text(MIME_MediaTypes.Text.plain);
entity_text_plain.Body = text_plain;
//普通文本郵件內(nèi)容,如果對方的收件客戶端不支持HTML,這是必需的
string plainTextBody = "如果你郵件客戶端不支持HTML格式,或者你切換到“普通文本”視圖,將看到此內(nèi)容";
if (!string.IsNullOrEmpty(SystemConfig.Default.PlaintTextTips))
{
plainTextBody = SystemConfig.Default.PlaintTextTips;
}
text_plain.SetText(MIME_TransferEncodings.QuotedPrintable, Encoding.UTF8, plainTextBody);
multipartAlternative.BodyParts.Add(entity_text_plain);
//--- text/html -----------------------------------------
string htmlText = mailInfo.Content;//"html>這是一份測試郵件,img src=\"cid:test.jpg\">來自font color=red>b>LumiSoft.Net/b>/font>/html>";
MIME_Entity entity_text_html = new MIME_Entity();
MIME_b_Text text_html = new MIME_b_Text(MIME_MediaTypes.Text.html);
entity_text_html.Body = text_html;
text_html.SetText(MIME_TransferEncodings.QuotedPrintable, Encoding.UTF8, htmlText);
multipartAlternative.BodyParts.Add(entity_text_html);
//--- application/octet-stream -------------------------
foreach (string attach in mailInfo.Attachments)
{
multipartMixed.BodyParts.Add(Mail_Message.CreateAttachment(attach));
}
foreach (string imageFile in mailInfo.EmbedImages)
{
MIME_Entity entity_image = new MIME_Entity();
entity_image.ContentDisposition = new MIME_h_ContentDisposition(MIME_DispositionTypes.Inline);
string fileName = DirectoryUtil.GetFileName(imageFile, true);
entity_image.ContentID = BytesTools.BytesToHex(Encoding.Default.GetBytes(fileName));
MIME_b_Image body_image = new MIME_b_Image(MIME_MediaTypes.Image.jpeg);
entity_image.Body = body_image;
body_image.SetDataFromFile(imageFile, MIME_TransferEncodings.Base64);
multipartMixed.BodyParts.Add(entity_image);
}
#endregion
return msg;
}
如果使用普通的賬號方式發(fā)送SMTP郵件,主要代碼如下所示,其中可以看出是利用了命令方式一步步和服務(wù)器進(jìn)行交互的。
復(fù)制代碼 代碼如下:
using (SMTP_Client client = new SMTP_Client())
{
int port = domainInfo.Ssl ? WellKnownPorts.SMTP_SSL : WellKnownPorts.SMTP;
if (domainInfo.Port > 0)
{
port = domainInfo.Port;
}
client.Connect(domainInfo.SmtpServer, port, domainInfo.Ssl);
client.Authenticate(domainInfo.Username, domainInfo.Password);
//string text = client.GreetingText;
client.MailFrom(mailFrom, -1);
client.RcptTo(mailTo);
MemoryStream stream = Create_Html_Attachment_Image(mailTo, mailFrom, mailFromDisplay);
client.SendMessage(stream);
client.Disconnect();
}
其中構(gòu)造郵件內(nèi)容的代碼和剛才的部分類似,詳細(xì)代碼如下所示。
復(fù)制代碼 代碼如下:
private MemoryStream Create_Html_Attachment_Image(string mailTo, string mailFrom, string mailFromDisplay)
{
Mime m = new Mime();
MimeEntity mainEntity = m.MainEntity;
mainEntity.From = new AddressList();
mainEntity.From.Add(new MailboxAddress(mailFromDisplay, mailFrom));
mainEntity.To = new AddressList();
mainEntity.To.Add(new MailboxAddress(mailTo, mailTo));
mainEntity.Subject = mailInfo.Title;
mainEntity.ContentType = MediaType_enum.Multipart_mixed;
MimeEntity textEntity = mainEntity.ChildEntities.Add();
textEntity.ContentType = MediaType_enum.Text_html;
textEntity.ContentTransferEncoding = ContentTransferEncoding_enum.QuotedPrintable;
textEntity.DataText = mailInfo.Content;
.........................
MemoryStream msg = new MemoryStream();
m.ToStream(msg);
msg.Position = 0;
return msg;
}
利用Lumisoft.NET這個組件,可以實現(xiàn)很多相關(guān)的郵件操作,這里介于興趣及篇幅原因,主要介紹郵件發(fā)送的功能模塊,其中貼出的代碼,一個是為了和感興趣的朋友相互交流,一個也是為了自己今后做一個借鑒,并不鼓勵大家用此軟件或者代碼來大批量發(fā)送垃圾郵件。
您可能感興趣的文章:- 深入Lumisoft.NET組件與.NET API實現(xiàn)郵件發(fā)送功能的對比分析
- 在asp.NET 中使用SMTP發(fā)送郵件的實現(xiàn)代碼
- Asp.net發(fā)送郵件的兩種方法小結(jié)
- asp.net System.Net.Mail 發(fā)送郵件
- .net發(fā)送郵件實現(xiàn)代碼
- Asp.Net類庫中發(fā)送電子郵件的代碼
- asp.net2.0實現(xiàn)郵件發(fā)送(測試成功)
- 在ASP.NET2.0中通過Gmail發(fā)送郵件的代碼
- 用ASP.NET做的個性化的郵件發(fā)送系統(tǒng)
- C#.NET發(fā)送郵件的實例代碼