BizTalk Server 2016接收Zip文件解压失败问题排查及方案咨询
BizTalk Server 2016 接收管道解压问题排查与替代方案
一、现有自定义解压组件问题排查
1. 代码逻辑核心问题
- 上下文属性操作错误:循环中修改**原输入消息(pInMsg)**的上下文并克隆给输出消息,会导致所有输出消息的上下文被覆盖;且应直接在新创建的
outMessage上下文上推广属性,而非原消息。 - 流处理不规范:
- 未重置原消息流
msgBodyPartStream的位置,可能导致ZipInputStream读取不全; - 直接将
MemoryStream赋值给outMessage.BodyPart.Data,未使用BizTalk的ReadOnlySeekableStream包装,可能导致流被重复读取或损坏。
- 未重置原消息流
- 文件名处理漏洞:当压缩包内文件无扩展名时,
entry.FileName.IndexOf(".")返回-1,Substring会抛出异常,中断解压流程,可能直接返回原Zip流。 - 第三方库潜在问题:Ionic.Zip(DotNetZip)旧版本存在
ZipInputStream读取bug,建议升级至最新稳定版。
2. 配置与注册问题
- 确认组件已正确注册至GAC,且接收管道的Disassemble阶段已添加该组件,无其他冲突的Disassembler组件。
- 检查接收管道是否被正确关联到接收位置。
修复后的核心代码片段
public void Disassemble(IPipelineContext pContext, IBaseMessage pInMsg) { IBaseMessagePart msgBodyPart = pInMsg.BodyPart; if (msgBodyPart != null) { Stream msgBodyPartStream = msgBodyPart.GetOriginalDataStream(); if (msgBodyPartStream != null) { // 重置原流位置,确保从开头读取 msgBodyPartStream.Position = 0; using (ZipInputStream zipInputStream = new ZipInputStream(msgBodyPartStream)) { ZipEntry entry = zipInputStream.GetNextEntry(); while (entry != null) { MemoryStream memStream = new MemoryStream(); byte[] buffer = new Byte[4096]; // 增大缓冲区提升效率 int bytesRead; while ((bytesRead = zipInputStream.Read(buffer, 0, buffer.Length)) > 0) { memStream.Write(buffer, 0, bytesRead); } // 创建输出消息 IBaseMessage outMessage = pContext.GetMessageFactory().CreateMessage(); IBaseMessagePart outBodyPart = pContext.GetMessageFactory().CreateMessagePart(); outMessage.AddPart("Body", outBodyPart, true); // 使用BizTalk包装类处理流,确保可重入 memStream.Position = 0; outBodyPart.Data = new ReadOnlySeekableStream(memStream); // 安全处理扩展名 string extension = string.Empty; int dotIndex = entry.FileName.LastIndexOf("."); if (dotIndex != -1 && dotIndex < entry.FileName.Length -1) { extension = entry.FileName.Substring(dotIndex); } // 在输出消息上下文上推广属性 outMessage.Context = PipelineUtil.CloneMessageContext(pInMsg.Context); outMessage.Context.Promote("Extension", "https://DemoZip.ZipMessageProperties", extension); OutFiles.Enqueue(outMessage); entry = zipInputStream.GetNextEntry(); } } } } }
二、BizTalk接收管道解压的其他方法
1. 使用.NET原生ZipArchive(无第三方依赖)
改用System.IO.Compression.ZipArchive(.NET 4.6+支持,BizTalk 2016兼容)实现解压,避免第三方库依赖:
public void Disassemble(IPipelineContext pContext, IBaseMessage pInMsg) { IBaseMessagePart msgBodyPart = pInMsg.BodyPart; if (msgBodyPart != null) { Stream msgBodyPartStream = msgBodyPart.GetOriginalDataStream(); msgBodyPartStream.Position = 0; using (ZipArchive archive = new ZipArchive(msgBodyPartStream, ZipArchiveMode.Read)) { foreach (ZipArchiveEntry entry in archive.Entries) { if (string.IsNullOrEmpty(entry.Name)) continue; // 跳过目录 MemoryStream memStream = new MemoryStream(); using (Stream entryStream = entry.Open()) { entryStream.CopyTo(memStream); } memStream.Position = 0; // 创建输出消息逻辑同修复后的代码 // ... } } } }
2. 接收位置预处理脚本(PowerShell)
在接收位置的预处理脚本中调用PowerShell解压文件,再由BizTalk处理解压后的内容:
$zipPath = $Env:BT_INPUT_FILE $extractPath = [System.IO.Path]::GetDirectoryName($zipPath) Add-Type -AssemblyName System.IO.Compression.FileSystem [System.IO.Compression.ZipFile]::ExtractToDirectory($zipPath, $extractPath) # 删除原Zip文件 Remove-Item $zipPath -Force
注意:需确保BizTalk宿主账户有足够权限读取、写入目标目录。
3. 使用社区BizTalk组件
利用BizTalk社区维护的开源组件(如BizTalk Factory的Zip组件),无需自行开发,直接在管道中配置使用。
内容的提问来源于stack exchange,提问作者Harsh Shelar
相关产品推荐
相关产品推荐

