BizTalk自定义发送管道仅解压单个消息而非全部的问题
问题根源
你的代码逻辑存在错误:循环处理Zip内每个文件时,反复将原消息pInMsg的Body数据替换为当前解压后的内容,最后仅返回这一条被多次覆盖的原消息,自然只会输出最后一个解压文件的内容。
BizTalk发送管道的预组装阶段要生成多条消息,必须通过消息队列+多次调用Execute方法的机制实现——管道框架会反复调用Execute,直到返回null才停止处理。
修复后的代码
[Guid("885186F0-8846-4AD1-BC3F-D0066844B871")] [ComponentCategory(CategoryTypes.CATID_Any)] [ComponentCategory(CategoryTypes.CATID_PipelineComponent)] public class UnzipperComponent : IBaseComponent, IComponent, IComponentUI, IPersistPropertyBag { private readonly Queue<IBaseMessage> _inboundMsgQueue = new Queue<IBaseMessage>(); IBaseMessage IComponent.Execute(IPipelineContext pContext, IBaseMessage pInMsg) { // 第一次调用时处理输入消息,生成所有解压消息到队列 if (pInMsg != null && _inboundMsgQueue.Count == 0) { using (var currentPartSource = pInMsg.BodyPart.GetOriginalDataStream()) { if (pInMsg.BodyPart != null && currentPartSource != null) { using (var seekableStream = new MemoryStream()) { currentPartSource.CopyTo(seekableStream); seekableStream.Seek(0, SeekOrigin.Begin); using (var unzipArchive = new ZipArchive(seekableStream, ZipArchiveMode.Read, true)) { foreach (var entry in unzipArchive.Entries) { // 跳过Zip里的目录条目 if (string.IsNullOrEmpty(entry.Name)) continue; using (var entryStream = entry.Open()) using (var outStream = new MemoryStream()) { entryStream.CopyTo(outStream); outStream.Seek(0, SeekOrigin.Begin); // 创建新消息实例 var newMsg = pContext.GetMessageFactory().CreateMessage(); newMsg.Context = PipelineUtil.CloneMessageContext(pInMsg.Context); var bodyPart = pContext.GetMessageFactory().CreateMessagePart(); bodyPart.Data = outStream; bodyPart.Charset = "UTF-8"; // 根据文件类型设置ContentType,可按需扩展判断逻辑 bodyPart.ContentType = entry.Name.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase) ? "application/pdf" : entry.Name.EndsWith(".xml", StringComparison.OrdinalIgnoreCase) ? "application/xml" : "text/plain"; // 用当前UTC时间生成唯一文件名后缀 var timestamp = DateTime.UtcNow.ToString("yyyyMMddHHmmssfff"); newMsg.Context.Promote( "ReceivedFileName", "http://schemas.microsoft.com/BizTalk/2003/file-properties", $"{entry.Name}_{timestamp}"); newMsg.AddPart("Body", bodyPart, true); // 注册资源到上下文,避免BizTalk回收时泄漏 pContext.ResourceTracker.AddResource(outStream); _inboundMsgQueue.Enqueue(newMsg); } } } } } } } // 每次调用返回队列中的一条消息,队列为空时返回null结束处理 return _inboundMsgQueue.Count > 0 ? _inboundMsgQueue.Dequeue() : null; } // 其他接口(IComponentUI、IPersistPropertyBag等)的实现代码保持原有逻辑即可,此处省略 }
关键修改点
- 用队列存储所有生成的解压消息,而非覆盖原消息
- 首次调用Execute时处理输入Zip文件,将所有解压后的消息加入队列
- 后续Execute调用从队列中取消息返回,直到队列为空返回null
- 修复细节问题:
- 跳过Zip中的目录条目,避免生成空消息
- 用
using包裹entry.Open()返回的流,确保资源释放 - 替换无效的DateTime初始化,用当前UTC时间生成唯一文件名后缀
- 根据文件类型动态设置ContentType
- 注册MemoryStream到ResourceTracker,防止资源泄漏
内容的提问来源于stack exchange,提问作者Hans Peter Eriksson
相关产品推荐
相关产品推荐

