淺談Exchange 2013開發-如何操作郵件的附件


因為項目中客戶有一個的要求,所以這個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帳號對這個文件夾有寫入的權限,然后把文件保存到這個文件里。

不足之處請大家指出...


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM