因為項目中客戶有一個的要求,所以這個Exchange前段時間搞的我很是頭疼,沒接觸過這個東西,但是現在看來,紙老虎一個。希望我的經驗可以幫助初次接觸它的人少走一些彎路!
簡單介紹一下:客戶要求在自己的Exchange生產環境上創建一個傳輸規則,當用戶郵件中包含有pdf類型的附件時,將這個附件下載到本地服務器,然后在加上可預覽這個附件的鏈接(服務商提供的web應用程序而不是微軟提供的的Office Web App Services)。
環境:一台DC,一台Exchange Server 2013服務器,建議8G內存至少
----------------------------------------------------------------------------------------
如何把郵件的附件Load下來網上應該很多源代碼,基本上都是使用Exchange Web Service,簡單說如下:
public void StripAttachments(ItemId id, string folder) { try { ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013); service.Credentials = new NetworkCredential("Administrator", "Password01!", "TEST.com"); service.Url = new Uri("https://192.168.1.116/ews/exchange.asmx"); EmailMessage email = EmailMessage.Bind(service, id); foreach (Attachment attachment in email.Attachments) { if (attachment is FileAttachment) { if (attachment.Name.Contains("pdf")) { // do your thing FileAttachment fileAttachment = attachment as FileAttachment; fileAttachment.Load("C:\\temp\\" + fileAttachment.Name); } } } } catch (Exception e) { // }
對已經存在於收件箱的郵件使用它操作最好不過了,但是不符合我的要求。因為我要對所有經過邊緣傳輸服務器的郵件進行操作。
微軟對於用戶自定義傳輸規則提供了兩個方式SmtpReceiveAgent和RoutingAgent,我使用了后者。
namespace Microsoft.Exchange.Data.Transport.Routing { public abstract class QueuedMessageEventArgs : EventArgs { public abstract MailItem MailItem { get; } } }
QueuedMessageEventArgs的MailItem為我們提供了操作郵件的支持
聲明一個繼承自RoutingAgentFactory的類,重寫CreateAgent方法
public sealed class MyRoutingFactory : RoutingAgentFactory { public override RoutingAgent CreateAgent(SmtpServer server) { RoutingAgent arcEmail = new EmailArchivingRoutingAgent(); return arcEmail; } }
聲明一個操作類
public class EmailArchivingRoutingAgent : RoutingAgent
構造方法中綁定觸發事件
public EmailArchivingRoutingAgent() { //Invoked by Exchange when the entire message has been Submitted. base.OnSubmittedMessage += new SubmittedMessageEventHandler(EmailArchivingRoutingAgent_OnSubmittedMessage); }
然后我們在EmailArchivingRoutingAgent_OnSubmittedMessage這個方法里面去寫對郵件的具體操作。通過循環e.MailItem.Message.Attachments,訪問所有附件對象,然后判斷類型再保存。
public void EmailArchivingRoutingAgent_OnSubmittedMessage(SubmittedMessageEventSource source, QueuedMessageEventArgs e) { for (int index = e.MailItem.Message.Attachments.Count - 1; index >= 0; index--) { //Get Attachment Microsoft.Exchange.Data.Transport.Email.Attachment atAttach = e.MailItem.Message.Attachments[index]; //Get FileName of this Attachment String feFileExtension = string.Empty; //Effective Attachment if (atAttach.AttachmentType == Microsoft.Exchange.Data.Transport.Email.AttachmentType.Regular & atAttach.FileName != null) { feFileExtension = atAttach.FileName.Substring((atAttach.FileName.Length - 4), 4); } //Judge the type of Attachment if (feFileExtension.ToLower() == ".pdf") { FileStream atFileStream = File.Create("E:\\Share\\" + atAttach.FileName); Stream attachstream = atAttach.GetContentReadStream(); byte[] bytes = ReadFully(attachstream, (int)attachstream.Length); //byte[] bytes = this.StreamToBytes(attachstream); atFileStream.Write(bytes, 0, bytes.Length); atFileStream.Close(); atFileStream = null; bytes = null; attachstream.Close(); attachstream = null; atAttach = null; //After load the Attachment,wirte a Link message into Body of mail if (File.Exists("E:\\Share\\" + e.MailItem.Message.Attachments[index].FileName)) { this.ChangeBodyOfMail(source, e,index); e.MailItem.Message.Subject += " PDF 寫在E盤了"; } } }
ReadFully是把GetContentReadStream()返回的流轉化為byte[]的方法
public static byte[] ReadFully(Stream stream, int initalLength) { if (initalLength < 1) { //min size initalLength = 32768; } byte[] buffer = new byte[initalLength]; int read = 0; int chunk; while ((chunk = stream.Read(buffer, read, buffer.Length - read)) > 0) { read += chunk; //if we've reached the end of our buffer,check to see if there's //any more information if (read == buffer.Length) { int nextByte = stream.ReadByte(); //End of stream?if so we're done if (nextByte == -1) { return buffer; } //nope,Resize the buffer,putin the byte we've just //read,and continue byte[] newBuffer = new byte[buffer.Length * 2]; Array.Copy(buffer, newBuffer, buffer.Length); newBuffer[read] = (byte)nextByte; buffer = newBuffer; read++; } } //Buffer is now too big,shrink it byte[] ret = new byte[read]; Array.Copy(buffer, ret, read); return ret; }
當然,一般安全的寫法不是直接使用GetContentReadStream(),而是使用TryGetContentReadStream(),如下:
Stream stream = null; if(attachment.TryGetContentReadStream(out stream)) {操作}
然后把編譯生成的dll部署到Exchange生產環境即可。
管理員身份運行Exchange Management Shell,輸入如下:
net stop msexchangetransport #停止傳輸服務
install-transportagent -name "Myagent" -assemblypath C:\Agent\Agents.dll -transportagentfactory Agents.MyRoutingFactory
enable-transportagent -identity "Myagent"
net start msexchangetransport #啟動傳輸服務
再看看保存的pdf
補充一下:
如果發生不能寫入的問題:
是NTFS的權限問題,IIS的帳戶沒有權限在E盤根目錄寫入數據。
如果是臨時文件,建議把文件保存到臨時目錄里去,可以用System.IO.Path.GetTempPath方法得到臨時目錄的路徑。
如果不是臨時文件,建議專門在服務器上創建一個專用的文件夾,並且使IUSR_matchinename或NETWORK_SERVICE帳號對這個文件夾有寫入的權限,然后把文件保存到這個文件里。
不足之處請大家指出...