在自定义管道组件中解压缩文件






4.25/5 (3投票s)
如何在自定义接收管道组件中解压缩文件
引言
在本文中,我将介绍如何在 BizTalk 接收管道的拆装阶段开发自定义管道组件来解压缩文件。
此组件有一个属性,允许用户输入用于解压缩文件的密码。收到的存档中的所有文件都将被解压缩。
创建自定义管道组件的许多代码都是基础性的,可以使用向导轻松实现。核心代码位于 IDisassemblerComponent
区域,这是本文的最后一部分代码。
代码
此项目在 Visual Studio 中创建为新的类库,并使用强名称密钥文件进行签名。
添加了两个引用:Microsoft.BizTalk.Pipeline.dll 和 Ionic.Zip.dll。
此项目中的类重命名为 UnzipDisassembler
,代码以添加以下命名空间开始。
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.BizTalk.Message.Interop;
using Microsoft.BizTalk.Component.Interop;
using Ionic.Zip;
using System.IO;
在下面的代码中,我为类添加了两个属性。第一个表示这是一个管道组件,第二个属性将组件限制为只能在管道的拆装阶段使用。
管道组件实现的具体接口决定了该管道组件与其他组件的区别。一旦组件实现了接口,它就可以被 BizTalk 运行时调用。所有组件和组件类别都位于 Microsoft.BizTalk.Component.Interop
命名空间中。拆装器管道组件需要实现 IBaseComponent
、IComponentUI
和 IDisassemblerComponent
接口。此外,下面的类还实现了 IPersistPropertyBag
接口,但这并非必需。
namespace Stm.UnzipDisassembler
{
[ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
[ComponentCategory(CategoryTypes.CATID_DisassemblingParser)]
[System.Runtime.InteropServices.Guid("90E45487-0AFC-49F5-81EB-737635678A17")]
public class UnzipDisassembler :
IBaseComponent, IComponentUI, IDisassemblerComponent, IPersistPropertyBag
{
在类内部,我实现了所有接口所需的属性和方法。
我从 IBaseComponent
接口开始。该接口提供了有关组件的基本信息的属性。
#region IBaseComponent
private const string _description = "Pipeline component used to unzip messages";
private const string _name = "UnzipDisassembler";
private const string _version = "1.0.0.0";
public string Description
{
get { return _description; }
}
public string Name
{
get { return _name; }
}
public string Version
{
get { return _version; }
}
#endregion
IComponentUI
接口定义了在管道设计器环境中使用的方法和属性。为保持简单,我在此处未提供任何代码。
#region IComponentUI
private IntPtr _icon = new IntPtr();
public IntPtr Icon
{
get { return _icon; }
}
public System.Collections.IEnumerator Validate(object projectSystem)
{
return null;
}
#endregion
接下来,我实现了 IPersistPropertyBag
接口。这是为了存储管道组件的属性信息。在接收管道中使用时,此组件允许用户在 BizTalk 管理控制台的接收管道配置的属性字段中输入密码。如果解压缩存档需要密码,则会使用此属性。
#region IPersistPropertyBag
private string _password;
public string Password
{
get { return _password; }
set { _password = value; }
}
public void GetClassID(out Guid classID)
{
classID = new Guid("625BBF88-0F86-419A-83AE-B15A976A6715");
}
public void InitNew()
{
}
public void Load(IPropertyBag propertyBag, int errorLog)
{
object val1 = null;
try
{
propertyBag.Read("Password", out val1, 0);
}
catch (ArgumentException)
{
}
catch (Exception ex)
{
throw new ApplicationException("Error reading PropertyBag: " + ex.Message);
}
if (val1 != null)
_password = (string)val1;
}
public void Save(IPropertyBag propertyBag, bool clearDirty, bool saveAllProperties)
{
object val1 = (object)_password;
propertyBag.Write("Password", ref val1);
}
#endregion
当解压缩存档需要密码时,将使用 Password
属性。
Load
方法将 PropertyBag
中的值读取到属性中。我在管道设计器中将组件添加到拆装阶段时遇到了一个错误消息。尽管组件运行正常,但为了避免此错误消息,我添加了一个 catch
块来 catch
ArgumentException
。
Save
方法将属性中的值写入 PropertyBag
。
GetClassID
返回组件的唯一标识值。
InitNew
用于初始化要持久化的对象到组件属性中。此项目不需要。
核心接口是 IDisassemblerComponent
。拆装器的目的是允许管道检查传入的文档并将其分解为更小的文档。
IDisassemblerComponent
有两个方法:Disassemble
和 GetNext
。BizTalk
运行时首先调用 Disassemble
方法,并将原始消息和管道上下文传递给它。然后,它调用 GetNext
方法,该方法返回类型为 IBaseMessage
的新消息,直到组件确定所有消息都已创建,然后返回 null
。
此组件在 Disassemble
方法中创建所有消息,并将它们放入队列结构中。每次调用 GetNext
方法时,它都会从队列中移除并返回一条消息。
#region IDisassemblerComponent
private System.Collections.Queue _qOutMessages = new System.Collections.Queue();
public void Disassemble(IPipelineContext pContext,IBaseMessage pInMsg)
{
IBaseMessagePart bodyPart = pInMsg.BodyPart;
if(bodyPart != null)
{
Stream originalStream = bodyPart.GetOriginalDataStream();
if(originalStream != null)
{
using (ZipInputStream zipInputStream = new ZipInputStream(originalStream))
{
if (_password != null)
if (_password.Length > 0)
zipInputStream.Password = _password;
ZipEntry entry = zipInputStream.GetNextEntry();
while (entry != null)
{
MemoryStream memStream = new MemoryStream();
byte[] buffer = new Byte[1024];
int bytesRead = 1024;
while (bytesRead != 0)
{
bytesRead = zipInputStream.Read(buffer, 0, buffer.Length);
memStream.Write(buffer, 0, bytesRead);
}
IBaseMessage outMessage;
outMessage = pContext.GetMessageFactory().CreateMessage();
outMessage.AddPart
("Body", pContext.GetMessageFactory().CreateMessagePart(), true);
memStream.Position = 0;
outMessage.BodyPart.Data = memStream;
outMessage.Context = PipelineUtil.CloneMessageContext(pInMsg.Context);
_qOutMessages.Enqueue(outMessage);
entry = zipInputStream.GetNextEntry();
}
}
}
}
}
public IBaseMessage GetNext(IPipelineContext pContext)
{
if (_qOutMessages.Count > 0)
return (IBaseMessage)_qOutMessages.Dequeue();
else
return null;
}
#endregion
}// Ends the class
}// Ends the namespace
上面的代码首先声明了一个用于消息的 private
队列。Disassmeble
方法检查输入流是否包含数据,然后从原始输入流创建一个 ZipInputStream
。可以为 ZipInputStream
设置密码。
while
循环中的代码(只要条目不为 null
就运行)将针对 ZipInputStream
中的每个条目运行。创建一个 MemoryStream
,称为 memStream
,然后从 zipInputStream
读取数据并写入 MemoryStream
。
当 memStream
完成后,将基于当前管道和传入消息的上下文创建一个消息 outMessage
。此消息的 BodyPart
的数据设置为 memStream
。该消息现在包含从传入的 zip 消息中提取的一个条目。该消息被放入队列。
zip 消息中的每个条目现在都是队列中的一条新消息。GetNext
方法在每次被调用时都会从队列中移除并返回这些消息。
安装和测试组件
要使用此组件,需要构建该项目,并在 BTS 接收管道项目中,必须将该组件 DLL 添加到工具箱中,并在拆装阶段使用它。Password
属性的值可以在管道设计时设置。部署管道后,也可以在 BTS 管理控制台的接收管道配置中设置此属性。
提示:如果在管道中使用组件后需要对其进行更改和重新构建,则在构建组件时可能会出现问题,因为 DLL 文件可能被另一个进程占用。要解决此问题,我使用 WhoLockedMe
应用程序来查看哪些进程锁定了文件,然后可以终止这些进程。WhoLockedMe
是免费的,很容易在互联网上搜索并下载。
另请参阅 在自定义管道组件中压缩文件。
历史
- 2011 年 2 月 1 日:初始发布