發送帶嵌入圖檔郵件之SMTP實作和ESMTP實作
接觸過郵件發送的人,可能對SMTP協定以及對dotnet下面的SMTP類的操作應該不會陌生,但可能不一定了解ESMTP是什麼東西,下面我們對它先做一個介紹,然後再詳細介紹兩種方式在發送嵌入圖檔(不是附件模式)操作的實作。說白了,ESMTP就是通過Socket流方式和郵件伺服器進行互動,基本上目前所有的伺服器都會提供對ESMTP協定的支援,SMTP協定在dotnet中實作就是通過調用System.Net.Mail .SmtpClient實作的。發送嵌入圖檔或者檔案郵件的原理就是,把圖檔資源送出(和附件類似方式,但是不同),然後通過修改HTML的IMG辨別的SRC實作實作内容的相對引用的。
接觸過郵件發送的人,可能對SMTP協定以及對dotnet下面的SMTP類的操作應該不會陌生,但可能不一定了解ESMTP是什麼東西,下面我們對它先做一個介紹,然後再詳細介紹兩種方式在發送嵌入圖檔(不是附件模式)操作的實作。
介紹SMTP指令和ESMTP過程
什麼是 SMTP
SMTP (Simple Mail Transfer Protocol) : 電子郵件從客戶機傳輸到伺服器或從某一個伺服器傳輸到另一個伺服器使用的傳輸協定。 SMTP 是請求/響應協定,指令和響應都是基于 ASCII 文本,并以 CR 和 LF 符結束。響應包括一個表示傳回狀态的三位數字代碼。SMTP 在 TCP 協定 25 端口監聽連接配接請求。
什麼是 ESMTP
ESMTP (Extended SMTP),顧名思義,擴充 SMTP 就是對标準 SMTP 協定進行的擴充。它與 SMTP 服務的差別僅僅是,使用 SMTP 發信不需要驗證使用者帳戶,而用 ESMTP 發信時, 伺服器會要求使用者提供使用者名和密碼以便驗證身份。驗證之後的郵件發送過程與 SMTP 方式沒有兩樣。
SMTP 指令
SMTP 指令包括:
HELO 向伺服器辨別使用者身份。發送者能欺騙,說謊,但一般情況下伺服器都能檢測到。
EHLO 向伺服器辨別使用者身份。發送者能欺騙,說謊,但一般情況下伺服器都能檢測到。
MAIL FROM 指令中指定的位址是發件人位址
RCPT TO 辨別單個的郵件接收人;可有多個 RCPT TO;常在 MAIL 指令後面。
DATA 在單個或多個 RCPT 指令後,表示所有的郵件接收人已辨別,并初始化資料傳輸,以 CRLF.CRLF 結束
VRFY 用于驗證指定的使用者/郵箱是否存在;由于安全方面的原因,伺服器常禁止此指令
EXPN 驗證給定的郵箱清單是否存在,擴充郵箱清單,也常被禁用
HELP 查詢伺服器支援什麼指令
NOOP 無操作,伺服器應響應 OK
RSET 重置會話,目前傳輸被取消
QUIT 結束會話
以上這些是SMTP和ESMPT的基本知識,特别是這些指令,對我們操作ESMTP協定幫助會比較大的,說白了,ESMTP就是通過Socket流方式和郵件伺服器進行互動,基本上目前所有的伺服器都會提供對ESMTP協定的支援,SMTP協定在dotnet中實作就是通過調用System.Net.Mail .SmtpClient實作的。
發送嵌入圖檔或者檔案郵件的原理就是,把圖檔資源送出(和附件類似方式,但是不同),然後通過修改HTML的IMG辨別的SRC實作實作内容的相對引用的。
如我們插入本地圖檔檔案的時候,檔案的位址是“D:\test.jpg",如果這樣發送出去,那麼肯定是檢視不到的了,我們要将它修改為:<img src=\"cid:test\">
然後,我們在郵件正文中附加相關的檔案就可以了,這個有點類似于一種特殊的定位格式。具體的示例代碼如下所示。
System.Net.Mail.MailMessage mailMessage = new System.Net.Mail.MailMessage();
mailMessage.From="發送者郵箱";
mailMessage.To.Add("收件人郵件清單");
mailMessage.CC.Add("抄送人郵件清單");
mailMessage.Subject = subject;
AlternateView htmlBody = AlternateView.CreateAlternateViewFromString(content,null,"text/html");
LinkedResource lrImage = new LinkedResource("test.jpg","image/gif");
lrImage.ContentId = "test";
htmlBody.LinkedResources.Add(lrImage);
mailMessage.AlternateViews.Add(htmlBody);
SmtpClient.Send(mailMessage);
其實對于調用SmtpClient發送嵌入圖檔檔案的操作,網上已經有不少文章介紹了,本人在尋找過程中,發現幾篇不錯的文章,不敢獨享,分享出來大家學習,下面提供幾篇介紹這方面知識的文章:
.NET C# 異步發送 要求回執 嵌入圖檔資源 自定義郵件頭 失敗通知 html/文本雙視圖 支援 notes 的郵件
.NET C# 發送郵件内容嵌入圖檔
Sending Emails in .NET with the System.Net.Mail Namespace
C#發送Email郵件三種方法的總結(轉載)
C#用于Windows程式的HTML編輯器(推薦,附源碼的)
以上都是基于SmtpClient類的實作,沒有發現有關ESMTP實作,即通過TCP Scoket進行發送嵌入圖檔檔案的郵件的方法。
其實在文章"c#發送需要smtp認證的郵件"(http://www.legalsoft.com.cn/docs/docs/17/577.html) 中,已經很詳細介紹了如何實作使用ESMTP協定與伺服器之間的互動了,其中還包括了發送郵件附件,算是比較詳細的ESMTP實作了。
其實實作發送ESMTP的嵌入圖檔,和發送附件方式有點類似了。
1、首先你需要編寫一個函數,用來解析發送的HTML内容,遇到有本地圖檔的,将檔案路徑替換為Cid:***格式,并将檔案寫入記憶體的Stream中備用,示例代碼如下所示。
private Hashtable EmbedList = new Hashtable(); //widened scope for MatchEvaluator
private string FixupReferences(string rawPayload, ref StringBuilder extras, string boundaryString)
{
//Build a symbol table to avoid redundant embedding.
Regex imgRE, linkRE, hrefRE;
MatchCollection imgMatches;
string imgMatchExpression = "(?:img[^>]+src\\s*=\\s*(?:\"(?<1>[^\"]*)\"|(?<1>\\S+))|url\\(['\"](?<1>[^'\"]*)['\"]\\))";
imgRE = new Regex(imgMatchExpression, RegexOptions.IgnoreCase | RegexOptions.Compiled);
string linkMatchExpression = "<\\s*link[^>]+href\\s*=\\s*(?:\"(?<1>[^\"]*)\"|(?<1>\\S+))[^>]*>";
linkRE = new Regex(linkMatchExpression, RegexOptions.IgnoreCase | RegexOptions.Compiled);
//this one's for fixup of relative urls in anchors
string refMatchExpression = "href\\s*=\\s*(?:['\"](?<1>[^\"]*)['\"]|(?<1>\\S+))";
hrefRE = new Regex(refMatchExpression, RegexOptions.IgnoreCase | RegexOptions.Compiled);
imgMatches = imgRE.Matches(rawPayload);
//translation to a Hashtable weeds out redundant references
foreach (Match m in imgMatches)
{
if (!EmbedList.ContainsKey(m.Groups[1].Value))
{
EmbedList.Add(m.Groups[1].Value, Guid.NewGuid());
}
}
//Prepare embedded data
extras.Length = 0;
string contentType;
ArrayList embeddees = new ArrayList(EmbedList.Keys);
foreach (string embeddee in embeddees)
contentType = embeddee.Substring(embeddee.LastIndexOf(".") + 1).ToLower();
extras.AppendFormat(boundaryString);
if (contentType.Equals("jpg")) contentType = "jpeg";
switch (contentType)
case "jpeg":
case "gif":
case "png":
case "bmp":
extras.AppendFormat("Content-Type: image/{0}; charset=\"iso-8859-1\"\r\n", contentType);
extras.Append("Content-Transfer-Encoding: base64\r\n");
extras.Append("Content-Disposition: inline\r\n");
extras.AppendFormat("Content-ID: <{0}>\r\n\r\n", EmbedList[embeddee]);
extras.Append(GetDataAsBase64(embeddee));
extras.Append("\r\n");
break;
//Fixups for references to items now embedded
rawPayload = imgRE.Replace(rawPayload, new MatchEvaluator(_fixup));
return rawPayload;
}
private string _fixup(Match m)
string replaceThis = m.Groups[1].Value;
string withThis = string.Format("cid:{0}", EmbedList[replaceThis]);
return m.Value.Replace(replaceThis, withThis);
然後你在和伺服器互動的時候,就可以通過這個函數,擷取解析後的HTML檔案内容和圖檔流,對其進行操作即可。
//判斷信件格式是否html
if (Html)
SendBufferstr += "Content-Type: text/html;" + enter;
else
SendBufferstr += "Content-Type: text/plain;" + enter;
//編碼資訊
if (Charset == "")
SendBufferstr += " charset=\"iso-8859-1\"" + enter;
SendBufferstr += " charset=\"" + Charset.ToLower() + "\"" + enter;
SendBufferstr += "Content-Transfer-Encoding: base64" + enter;
StringBuilder extras = new StringBuilder();
string extrasBoundary = "--" + boundary + enter;
string newBodyHtml = FixupReferences(this.Body, ref extras, extrasBoundary);
SendBufferstr += enter + enter;
SendBufferstr += B64StrLine(Base64Encode(newBodyHtml)) + enter;
SendBufferstr += enter + "--" + boundary1 + "--" + enter + enter;
SendBufferstr += extras.ToString();
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIml2ZuUmbv50LcNncvRXYjlGZul0ZulmbpxGd190LcNXZnFWbJ9CXt92YuM3ZvxmYuNmL3d3dvw1LcpDc0RHaiojIsJye.gif)
主要研究技術:代碼生成工具、會員管理系統、客戶關系管理軟體、病人資料管理軟體、Visio二次開發、酒店管理系統、倉庫管理系統等共享軟體開發
專注于Winform開發架構/混合式開發架構、Web開發架構、Bootstrap開發架構、微信門戶開發架構的研究及應用。
轉載請注明出處:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIml2ZuUmbv50LcNncvRXYjlGZul0ZulmbpxGd190LcNXZnFWbJ9CXt92YuM3ZvxmYuNmL3d3dvw1LcpDc0RHaiojIsJye.gif)
撰寫人:伍華聰