65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.20/5 (2投票s)

2011年2月1日

CPOL

4分钟阅读

viewsIcon

37389

downloadIcon

1135

如何在自定义发送管道组件中压缩文件

引言

在本文中,我将介绍如何在 BizTalk 发送管道的编码阶段开发一个自定义管道组件来压缩消息。

该组件有两个属性,允许用户为 zip 文件键入密码,并为压缩文件中的条目键入扩展名。使用此组件的发送管道的结果将是一个 zip 文件,其中包含带有指定扩展名的消息。

创建自定义管道组件的许多代码都很基础,向导可以轻松实现。最重要的代码位于本文最后一个代码块的 IComponent 区域。

代码

此项目在 Visual Studio 中创建为新的类库,并使用强名称密钥文件进行签名。

添加了两个引用:Microsoft.BizTalk.Pipeline.dllIonic.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 命名空间中。编码器管道组件需要实现 IBaseComponentIComponentUI 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 写入 MemoryStreammemStream)。循环结束后,memStream 将包含压缩数据。

在使用 memStream 返回消息 pInMsg 时遇到了一些问题。我通过从 memStream 获取数据并将其放入缓冲区,然后使用该缓冲区创建新的 MemoryStream 来解决此问题,以便在返回数据时使用。数据在这里以高比例压缩,我这样做没有遇到问题。

最后,代码返回包含 zip 存档的 pInMsg

安装和测试组件

要使用此组件,需要生成项目,然后在 BTS 发送管道项目中,必须将此组件 DLL 添加到工具栏并在编码阶段使用。Password EntryExtension 属性的值可以在管道设计时设置。管道部署后,这些属性也可以在 BTS 管理控制台中发送管道的配置中设置。

提示:如果在管道中使用组件后需要对其进行更改和重新生成,则在生成组件时可能会遇到问题,因为 DLL 文件可能被另一个进程使用。为了解决这个问题,我使用 WhoLockedMe 应用程序来查看哪些进程锁定了文件,然后可以终止这些进程。WhoLockedMe 是免费的,可以在互联网上轻松搜索到并下载。

另请参阅 在自定义管道组件中解压文件

历史

  • 2011 年 2 月 1 日:初始发布
© . All rights reserved.