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






4.20/5 (2投票s)
如何在自定义发送管道组件中压缩文件
引言
在本文中,我将介绍如何在 BizTalk 发送管道的编码阶段开发一个自定义管道组件来压缩消息。
该组件有两个属性,允许用户为 zip 文件键入密码,并为压缩文件中的条目键入扩展名。使用此组件的发送管道的结果将是一个 zip 文件,其中包含带有指定扩展名的消息。
创建自定义管道组件的许多代码都很基础,向导可以轻松实现。最重要的代码位于本文最后一个代码块的 IComponent
区域。
代码
此项目在 Visual Studio 中创建为新的类库,并使用强名称密钥文件进行签名。
添加了两个引用:Microsoft.BizTalk.Pipeline.dll 和 Ionic.Zip.dll。
此项目中的类被重命名为 ZipEncoder
,代码通过添加以下命名空间开始
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using Microsoft.BizTalk.Message.Interop;
using Microsoft.BizTalk.Component.Interop;
using Ionic.Zip;
在下面的代码中,我向类添加了两个属性。第一个属性表示这是一个管道组件,第二个属性将组件限制只能在管道的编码阶段使用。
管道组件实现的特定接口是区分不同管道组件的关键。一旦组件实现了接口,就可以被 BizTalk 运行时调用。所有组件和组件类别都位于 Microsoft.BizTalk.Component.Interop
命名空间中。编码器管道组件需要实现 IBaseComponent
、IComponentUI
和 IComponent
接口。此外,下面的类实现了 IPersistPropertyBag
接口,但这并不是必需的。
namespace Stm.ZipEncoder
{
[ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
[ComponentCategory(CategoryTypes.CATID_Encoder)]
[System.Runtime.InteropServices.Guid("2cbc0379-c573-4506-9980-da69aad1496c")]
public class ZipEncoder : IBaseComponent, IComponentUI,
IComponent, IPersistPropertyBag
{
在类内部,我实现了所有接口所需的属性和方法。
我从 IBaseComponent
接口开始。该接口提供了有关组件的基本信息的属性。
#region IBaseComponent
private const string _description = "Pipeline component used to zip a message";
private const string _name = "ZipEncoder";
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;
private string _entryExtension;
public string Password
{
get { return _password; }
set { _password = value; }
}
public string EntryExtension
{
get { return _entryExtension; }
set { _entryExtension = value; }
}
public void GetClassID(out Guid classID)
{
classID = new Guid("0c2cc78f-c88e-40db-a9ba-072c354af57f");
}
public void InitNew()
{
}
public void Load(IPropertyBag propertyBag, int errorLog)
{
object val1 = null;
object val2 = null;
try
{
propertyBag.Read("Password", out val1, 0);
propertyBag.Read("EntryExtension", out val2, 0);
}
catch (ArgumentException)
{
}
catch (Exception ex)
{
throw new ApplicationException("Error reading PropertyBag: " + ex.Message);
}
if (val1 != null)
_password = (string)val1;
if (val2 != null)
_entryExtension = (string)val2;
else
_entryExtension = "xml";
}
public void Save(IPropertyBag propertyBag, bool clearDirty, bool saveAllProperties)
{
object val1 = (object)_password;
object val2 = (object)_entryExtension;
propertyBag.Write("Password", ref val1);
propertyBag.Write("EntryExtension", ref val2);
}
#endregion
Password
属性用于为 zip 存档设置密码。EntryExtension
属性用于为存档中的文件设置扩展名。
Load
方法将 PropertyBag
中的值读取到属性中。我在管道设计器中将组件添加到编码阶段时遇到了一个错误消息。然而,该组件运行正常,但为了避免此错误消息,我添加了一个 catch
块来捕获 ArgumentException
。
Save
方法将属性中的值写回 PropertyBag
。
GetClassID
返回组件的唯一标识值。
InitNew
用于初始化要持久化的对象到组件属性中。此项目不需要。
核心接口是 IComponent
。在此项目中,它包含一个方法,该方法执行管道组件以处理输入消息并获取结果消息。
#region IComponent
public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg)
{
IBaseMessagePart bodyPart = pInMsg.BodyPart;
if (bodyPart != null)
{
Stream originalStream = bodyPart.GetOriginalDataStream();
if (originalStream != null)
{
MemoryStream memStream = new MemoryStream();
using(ZipOutputStream zipOutputStream = new ZipOutputStream(memStream))
{
byte[] buffer = new Byte[1024];
zipOutputStream.CompressionLevel =
Ionic.Zlib.CompressionLevel.BestCompression;
if (_password != null)
zipOutputStream.Password = _password;
if (_entryExtension != null)
if (_entryExtension.Length > 0)
_entryExtension = "." + _entryExtension;
zipOutputStream.PutNextEntry(pInMsg.MessageID + _entryExtension);
int bytesRead = 1024;
while (bytesRead != 0)
{
bytesRead = originalStream.Read(buffer, 0, buffer.Length);
zipOutputStream.Write(buffer, 0, bytesRead);
}
}
byte[] buff = memStream.GetBuffer();
MemoryStream ms = new MemoryStream(buff);
ms.Position = 0;
pInMsg.BodyPart.Data = ms;
}
}
return pInMsg;
}
#endregion
}// Ends the class
}// Ends the namespace
上面的代码首先检查输入流是否包含数据。如果是,则从 Ionic.Zip 命名空间创建一个 ZipOutputStream
对象,用于将压缩数据写入 MemoryStream
。来自 PropertyBag
的密码被设置为 zipOutputStream
,然后 zipOutputStream
开始写入一个新的 zip 文件条目,其名称由 MessageID
和 EntryExtension
属性创建。
在 while
循环中,所有数据都从原始输入流读取,并由 zipOutputStream
写入 MemoryStream
(memStream
)。循环结束后,memStream
将包含压缩数据。
在使用 memStream
返回消息 pInMsg
时遇到了一些问题。我通过从 memStream
获取数据并将其放入缓冲区,然后使用该缓冲区创建新的 MemoryStream
来解决此问题,以便在返回数据时使用。数据在这里以高比例压缩,我这样做没有遇到问题。
最后,代码返回包含 zip 存档的 pInMsg
。
安装和测试组件
要使用此组件,需要生成项目,然后在 BTS 发送管道项目中,必须将此组件 DLL 添加到工具栏并在编码阶段使用。Password
和 EntryExtension
属性的值可以在管道设计时设置。管道部署后,这些属性也可以在 BTS 管理控制台中发送管道的配置中设置。
提示:如果在管道中使用组件后需要对其进行更改和重新生成,则在生成组件时可能会遇到问题,因为 DLL 文件可能被另一个进程使用。为了解决这个问题,我使用 WhoLockedMe
应用程序来查看哪些进程锁定了文件,然后可以终止这些进程。WhoLockedMe
是免费的,可以在互联网上轻松搜索到并下载。
另请参阅 在自定义管道组件中解压文件。
历史
- 2011 年 2 月 1 日:初始发布