《Windows Azure Platform 系列文章目录》
前2章我们已经介绍了Windows Azure发送邮件的两种方法,分别是使用on-premise的Email Forward Service和Exchange Server。
现在我们介绍第三种方法:使用第三方SMTP服务。
源代码您可以在这里下载,有三点我们需要了解:
背景介绍:
在Windows Azure平台上,发送邮件的功能要比你想象的更复杂。
发送电子邮件对于Windows Azure这样的云平台提出了挑战,因为您没有一个专用的IP地址。那些发送垃圾邮件的人,非常有可能使用Windows Azure来发送垃圾邮件。一旦出现这样的情况,垃圾邮件黑名单会迅速将Windows Azure数据中心的IP地址范围标记为垃圾邮件的来源。这意味着您的合法电子邮件将无法正常发送。
对于这些挑战,最佳的解决方案是不从Windows Azure发送电子邮件。而是通过那些有严格的反垃圾邮件规则和专用IP地址的第三方SMTP服务(比如SendGid 或者AuthSMTP)。
请注意:接收电子邮件是另外一个故事。只要人们愿意发送电子邮件到您的域名,您都可以在Windows Azure接收,只要通过端口号25监听SMTP流量。
使用第三方服务发送电子邮件
我们首先要做的事情是在SendGrid注册一个免费的账户。免费账户允许我们每天发送200封电子邮件,但是他不给我们开放一些高级功能。我们建议您:如果你很关于您的邮件发送过程,请至少升级到银牌(Silver)用户,以保证更好的邮件发送质量。
一旦我们完成了快速注册,我需要记下所有的细节内容,以便我在配置文件里做添加。
实际上发送电子是非常容易的,我们可以使用System.Net.Mail这个命名空间。下面的代码是为EmailTheInternet.com发送电子邮件回复
var reply = new MailMessage(RoleEnvironment.GetConfigurationSettingValue("EmailAddress"), msg.FromAddress) { Subject = msg.Subject.StartsWith("RE:", StringComparison.InvariantCultureIgnoreCase) ? msg.Subject : string.Format("RE: " + msg.Subject), Body = body, IsBodyHtml = msg.HasHtmlBody // send HTML if we got HTML }; if (!reply.IsBodyHtml) reply.BodyEncoding = Encoding.UTF8; // make it a proper reply reply.Headers["References"] = msg.MessageID; reply.Headers["In-Reply-To"] = msg.MessageID; // use our SMTP server, port, username, and password to send the mail (new SmtpClient(RoleEnvironment.GetConfigurationSettingValue("SmtpServer"), int.Parse(RoleEnvironment.GetConfigurationSettingValue("SmtpPort"))) { Credentials = new NetworkCredential(RoleEnvironment.GetConfigurationSettingValue("SmtpUsername"), RoleEnvironment.GetConfigurationSettingValue("SmtpPassword")) }).Send(reply);
请注意:我用的是SendGrid的免费帐号,这意味着我每天只能发有限数量的邮件,我们没有一个固定IP地址和whitelabeling.正因为如此,我的邮件可能无法全部通过垃圾邮件过滤器,一些电子邮件客户端将显示我通过SendGrid发送的邮件为"代表 [email protected]"。这是因为我还没有为这些服务付费(我使用的是免费帐号),不是因为这种方法的缺陷。
使用Worker Role接收邮件
我很惊讶,在C#中找到为接收邮件功能的易用、免费的类库是非常困难的。我参考了Eric Daugherty’s C# Email Server (CSES),然后添加了SharpMimeTools,以处理复杂的、有MIME编码和附件任务。
您可以阅读代码的细节,但是我基本上做两件事情:
下面的代码初始化SMTP,然后启动TcpListener(在Onstart()调用)。
listener = new TcpListener(IPAddress.Any, RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["SmtpIn"].IPEndpoint.Port); processor = new SMTPProcessor(RoleEnvironment.GetConfigurationSettingValue("DomainName"), new RecipientFilter(), new MessageSpool()); listener.Start();
请注意:我使用service runtime API来确定正确的端口。
下面是简单的异步处理传入的TCP连接(在Run()调用)
var mutex = new ManualResetEvent(false); while (true) { mutex.Reset(); listener.BeginAcceptSocket((ar) => { mutex.Set(); processor.ProcessConnection(listener.EndAcceptSocket(ar)); }, null); mutex.WaitOne(); }
最后的代码是处理传入的电子邮件
// make a container, with public access to blobs var id = Guid.NewGuid().ToString().Replace("-", null); var container = account.CreateCloudBlobClient().GetContainerReference(id); container.Create(); container.SetPermissions(new BlobContainerPermissions() { PublicAccess=BlobContainerPublicAccessType.Blob }); // parse the message var msg = new SharpMessage(new MemoryStream(Encoding.ASCII.GetBytes(message.Data)), SharpDecodeOptions.AllowAttachments | SharpDecodeOptions.AllowHtml | SharpDecodeOptions.DecodeTnef); // create a permalink-style name for the blob var permalink = Regex.Replace(Regex.Replace(msg.Subject.ToLower(), @"[^a-z0-9]", "-"), "--+", "-").Trim('-'); if (string.IsNullOrEmpty(permalink)) { // in case there's no subject permalink = "message"; } var bodyBlob = container.GetBlobReference(permalink); // set the CDN to cache the object for 2 hours bodyBlob.Properties.CacheControl = "max-age=7200"; // replaces references to attachments with the URL of where we'll put them msg.SetUrlBase(Utility.GetCdnUrlForUri(bodyBlob.Uri) + "/[Name]"); // save each attachment in a blob, setting the appropriate content type foreach (SharpAttachment attachment in msg.Attachments) { var blob = container.GetBlobReference(permalink + "/" + attachment.Name); blob.Properties.ContentType = attachment.MimeTopLevelMediaType + "/" + attachment.MimeMediaSubType; blob.Properties.CacheControl = "max-age=7200"; attachment.Stream.Position = 0; blob.UploadFromStream(attachment.Stream); } // add the footer and save the body to the blob SaveBody(msg, bodyBlob, message, container, permalink);
参考资料 http://blog.smarx.com/posts/emailtheinternet-com-sending-and-receiving-email-in-windows-azure