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






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 日:初始发布


