BizTalk无编排自定义发送管道组件实现带附件SMTP邮件发送问题
核心问题排查
你的消息卡在「已排队(等待处理)」及功能异常由以下几类错误导致:
- 输入流读取后未重置位置:使用XPathDocument读取输入消息的BodyPart流后,流指针停在末尾,后续管道组件无法读取消息内容,是消息挂起的核心原因
- 正文内容未写入流:仅配置了正文相关属性,未将实际正文内容写入BodyPart的数据流
- 正文ContentType配置错误:
text/application为非法格式,纯文本正文应使用text/plain,HTML正文应使用text/html - 附件对象重复创建问题:仅在循环外创建一次消息Part对象,导致所有附件最终都为最后一个附件的内容
- SMTP属性配置位置错误:Subject、EmailBodyText等SMTP属性需配置在消息上下文而非消息Part的属性中,SMTP适配器无法识别Part属性中的配置
- 数据流位置未重置:所有新创建的MemoryStream写入内容后未将指针重置到起始位置,后续读取会返回空内容
- 变量拼写错误:Attachment类的文件名属性拼写混乱,会引发空引用或文件名错误
修正后完整代码
using System; using System.Collections.Generic; using System.IO; using System.Xml.XPath; using Microsoft.BizTalk.Component.Interop; using Microsoft.BizTalk.Message.Interop; public class CustomSendPipelineComponent : IBaseComponent, IComponent, IComponentUI { public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg) { XPathNavigator nav = ReadXmlFromMsgBox(pInMsg); var outMsg = pContext.GetMessageFactory().CreateMessage(); outMsg.Context = PipelineUtil.CloneMessageContext(pInMsg.Context); GetRegistratorProperties(nav, pContext, outMsg); var attachments = GetAttachments(nav); ProcessAttachments(pContext, outMsg, attachments); return outMsg; } private void GetRegistratorProperties(XPathNavigator _nav, IPipelineContext _pContext, IBaseMessage _msg) { // 从XML读取UUID作为正文内容 string uuid = _nav.SelectSingleNode("ArchiveobjectsListErrands/ArchiveobjectErrand/UUID")?.Value ?? "无业务UUID"; string emailBody = $"业务单据UUID:{uuid}\r\n业务处理完成,附件请查收"; var bodyPart = _pContext.GetMessageFactory().CreateMessagePart(); bodyPart.ContentType = "text/plain;charset=utf-8"; // 写入正文内容到流 byte[] bodyBytes = System.Text.Encoding.UTF8.GetBytes(emailBody); bodyPart.Data = new MemoryStream(bodyBytes); bodyPart.Data.Seek(0, SeekOrigin.Begin); // SMTP属性写入消息上下文,而非Part属性 _msg.Context.Write("EmailBodyText", "http://schemas.microsoft.com/BizTalk/2003/smtp-properties", emailBody); _msg.Context.Write("Subject", "http://schemas.microsoft.com/BizTalk/2003/smtp-properties", "Registratorsubject - Create errand"); _msg.Context.Write("SMTPMessageBody", "http://schemas.microsoft.com/BizTalk/2003/smtp-properties", "1"); // 标记使用自定义正文 _msg.AddPart("Body", bodyPart, true); } private void ProcessAttachments(IPipelineContext _pContext, IBaseMessage _msg, IList<Attachment> _attachments) { int i = 0; foreach (var item in _attachments) { // 每次循环新建独立的Part对象 var msgPart = _pContext.GetMessageFactory().CreateMessagePart(); msgPart.PartProperties.Write("FileName", "http://schemas.microsoft.com/BizTalk/2003/mime-properties", $"{item.FileName}{item.Extension}"); msgPart.PartProperties.Write("ContentDescription", "http://schemas.microsoft.com/BizTalk/2003/mime-properties", item.ContentType); byte[] attachmentBytes = BytesFromBase64String(item.Base64String); msgPart.Data = new MemoryStream(attachmentBytes); msgPart.Data.Seek(0, SeekOrigin.Begin); msgPart.ContentType = item.ContentType; _msg.AddPart($"Attachment part {i}", msgPart, false); i++; } } private IList<Attachment> GetAttachments(XPathNavigator _nav) { // 替换为你实际的附件XPath路径 XPathNodeIterator iterator = _nav.Select("ArchiveobjectsListErrands/ArchiveobjectErrand/ArchiveobjectListPaper/Attachments"); IList<Attachment> myList = new List<Attachment>(); while (iterator.MoveNext()) { XPathNavigator node = iterator.Current; Attachment attachment = new Attachment(); attachment.FileName = node.SelectSingleNode("Name")?.Value ?? "未命名附件"; attachment.Extension = node.SelectSingleNode("Extension")?.Value ?? ".dat"; attachment.ContentType = node.SelectSingleNode("Mimetype")?.Value ?? "application/octet-stream"; attachment.Base64String = node.SelectSingleNode("Base64String")?.Value ?? ""; if(!string.IsNullOrEmpty(attachment.Base64String)) myList.Add(attachment); } return myList; } private XPathNavigator ReadXmlFromMsgBox(IBaseMessage pInMsg) { Stream originalMessage = pInMsg.BodyPart.GetOriginalDataStream(); XPathNavigator _navigator = new XPathDocument(originalMessage).CreateNavigator(); // 读取完成后重置输入流位置,供后续组件使用 originalMessage.Seek(0, SeekOrigin.Begin); return _navigator; } private static byte[] BytesFromBase64String(string base64Str) { if(string.IsNullOrEmpty(base64Str)) return Array.Empty<byte>(); return Convert.FromBase64String(base64Str); } [Serializable] private class Attachment { public string FileName { get; set; } public string Extension { get; set; } public string ContentType { get; set; } public string Base64String { get; set; } } #region 管道组件基础实现(保留向导生成的内容即可) public string Name => "自定义SMTP发送管道组件"; public string Version => "1.0.0.0"; public string Description => "处理Base64附件并生成SMTP邮件"; public IntPtr Icon => IntPtr.Zero; public System.Collections.IEnumerator Validate(object projectSystem) => null; #endregion }
部署验证注意事项
- 发送管道编码阶段组件顺序:自定义组件在前,MIME/SMIME编码器在后
- SMTP适配器配置:需开启「正文为MIME部分」选项,否则自定义添加的附件不会被识别
- 组件部署:更新代码后需将DLL加入GAC,重启对应主机实例后再测试
内容的提问来源于stack exchange,提问作者Emperor 2052
相关产品推荐
相关产品推荐

