浅谈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帐号对这个文件夹有写入的权限,然后把文件保存到这个文件里。
不足之处请大家指出...