使用 BizTalk 传输大文件 - 接收端






4.33/5 (4投票s)
如何使用 BizTalk 传输大文件 - 接收端
引言
在 BizTalk 中处理大文件(200MB 以上)可能会成为一个主要的性能瓶颈,当一个文件被接收管道解析并存储到 MessageBox
中时。接收文件的 BizTalk Server 主机实例和 SQL Server 的 CPU 使用率可能会非常高,从而减慢系统速度,尤其是在同时接收更多大文件时。
解决此问题的一种方法是创建一个自定义管道组件,该组件接收大文件,将其存储到磁盘,并创建一个包含大文件存储位置信息的小型 XML 消息。小型 XML 消息将被存储到 MessageBox
中,而不是大文件。它包含与大文件相同的上下文信息,并且可以被编排或发送管道拾取。发送组件然后获取大文件的位置并执行发送过程。删除大文件可以安排在计划任务中进行。在本文中,我将介绍如何接收大文件、将其存储到磁盘并创建小型 XML 消息。
自定义管道组件
创建自定义管道组件的许多代码都是基础性的,也可以由向导生成。然而,我在下面列出了所有代码并进行了简要说明。核心代码位于实现了 IComponent
接口的 Execute
方法中。
自定义管道组件是在 Visual Studio 中作为新的类库创建的,并使用强名称密钥文件进行签名。
已添加对 Microsoft.BizTalk.Pipeline.dll 的引用。
代码首先添加以下命名空间以创建自定义管道组件并将其文件存储到磁盘。
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.BizTalk.Message.Interop;
using Microsoft.BizTalk.Component.Interop;
using System.IO;
类开头的代码如下所示。一个属性指示这是一个管道组件,第二个属性限制该组件只能在管道的解码阶段使用。解码器管道组件需要实现 IBaseComponent
、IComponentUI
和 IComponent
接口。此外,下面的类实现了 IPersistPropertyBag
接口,但这并不是必需的。
namespace Stm.LargeFileDecoder
{
[ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
[ComponentCategory(CategoryTypes.CATID_Decoder)]
[System.Runtime.InteropServices.Guid("53fd04d5-8337-42c2-99eb-32ac96d1105a")]
public class LargeFileDecoder : IBaseComponent,
IComponentUI, IComponent, IPersistPropertyBag
{
IBaseComponent
接口提供了提供组件基本信息的属性。
#region IBaseComponent
private const string _description =
"Pipeline component used to save large files to disk";
private const string _name = "LargeFileDecoder";
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 管理控制台的接收管道配置中的属性字段中输入值。
有两个属性可供用户使用。LargeFileLocation
是存储大文件的目录路径。ThresholdSize
是一个以字节为单位的值,用于确定是否将文件视为大文件。
#region IPersistPropertyBag
private string _largeFileLocation;
private int _thresholdSize;
public string LargeFileLocation
{
get { return _largeFileLocation; }
set { _largeFileLocation = value; }
}
public int ThresholdSize
{
get { return _thresholdSize; }
set { _thresholdSize = value; }
}
public void GetClassID(out Guid classID)
{
classID = new Guid("53fd04d5-8337-42c2-99eb-32ac96d1105a");
}
public void InitNew()
{
}
public void Load(IPropertyBag propertyBag, int errorLog)
{
object val1 = null;
object val2 = null;
try
{
propertyBag.Read("LargeFileLocation", out val1, 0);
propertyBag.Read("ThresholdSize", out val2, 0);
}
catch (ArgumentException)
{
}
catch (Exception ex)
{
throw new ApplicationException("Error reading PropertyBag: " + ex.Message);
}
if (val1 != null)
_largeFileLocation = (string)val1;
if (val2 != null)
_thresholdSize = (int)val2;
}
public void Save(IPropertyBag propertyBag, bool clearDirty, bool saveAllProperties)
{
object val1 = (object)_largeFileLocation;
propertyBag.Write("LargeFileLocation", ref val1);
object val2 = (object)_thresholdSize;
propertyBag.Write("ThresholdSize", ref val2);
}
#endregion
Load
方法将 PropertyBag
中的值读取到属性中。我在将组件添加到管道设计器中管道的拆解阶段时遇到了一个错误消息。但是,组件工作正常,但为了避免此错误消息,我添加了一个 catch
块来捕获 ArgumentException
。
Save
方法将属性中的值写入 PropertyBag
。
GetClassID
返回组件的唯一标识值。
InitNew
用于初始化要持久化的对象到组件属性中。此项目不需要。
核心接口是 IComponent
。在这种情况下,它包含一个方法,该方法执行管道组件以将输入消息存储到磁盘并创建小型 XML 消息以供进一步处理。
#region IComponent
public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg)
{
if (_largeFileLocation == null || _largeFileLocation.Length == 0)
_largeFileLocation = Path.GetTempPath();
if (_thresholdSize == null || _thresholdSize == 0)
_thresholdSize = 4096;
// Treat as large file only if the size of the file is greater than the thresholdsize
if(pInMsg.BodyPart.GetOriginalDataStream().Length > _thresholdSize)
{
Stream originalStream = pInMsg.BodyPart.GetOriginalDataStream();
string largeFilePath = _largeFileLocation + pInMsg.MessageID.ToString() + ".msg";
FileStream fs = new FileStream(largeFilePath, FileMode.Create);
// Write message to disk
byte[] buffer = new byte[1]; // Set to 1024 or 4096 for faster processing
int bytesRead = originalStream.Read(buffer, 0, buffer.Length);
while(bytesRead!=0)
{
fs.Flush();
fs.Write(buffer, 0, buffer.Length);
bytesRead = originalStream.Read(buffer, 0, buffer.Length);
}
fs.Flush();
fs.Close();
// Create a small xml message
string xmlInfo =
"<MsgInfo xmlns='http://Stm.LargeFileTransfer'><LargeFilePath>" +
largeFilePath + "</LargeFilePath></MsgInfo>";
byte[] byteArray = System.Text.Encoding.UTF8.GetBytes(xmlInfo);
MemoryStream ms = new MemoryStream(byteArray);
pInMsg.BodyPart.Data = ms;
}
return pInMsg;
}
#endregion
首先,如果用户未在 BizTalk 管理控制台中设置 LargeFileLocation
和 ThresholdSize
属性,则获取这些属性的值。仅当文件大小大于指定的 thresholdsize
时,才会将传入文件作为大文件处理。如果是这样,FileStream
会创建一个具有唯一名称的新文件,并将整个传入流写入该文件。这在一个循环中完成,首先将传入流的一部分读入缓冲区,然后将该缓冲区的内容写入 FileStream
。然后,将包含大文件路径的小型 XML 消息作为 string
创建。然后将此 string
转换为字节数组,用于创建 MemoryStream
。此 MemoryStream
现在是新的 XML 消息,并将其分配给传入消息 BodyPart
的 Data
属性。然后返回整个消息。
架构和接收管道
为了让 BizTalk Server 识别上面组件返回的小型 XML 消息,必须创建并部署一个架构。(然而,它不一定需要被识别。这取决于发送端如何处理消息。)创建架构可以通过从 XML string
创建 XML 文件并使用 Visual Studio 中创建架构的向导来生成架构来轻松完成。包含大文件路径的字段需要是一个区分字段,如果它将从编排访问,或者需要被提升,如果它将要在管道中访问。
当上述组件构建完成后,它可以存储在 BizTalk Server 上与其他管道组件相同的文件夹中。在 BTS 接收管道项目中,必须将此组件 DLL 添加到工具栏并在解码阶段使用。在同一个解码阶段,在此组件的正下方,可以使用 XML Disassembler 组件,以便识别自定义组件返回的小型 XML 消息。
BizTalk 上的接收端
当上述组件构建并部署后,BizTalk 上的接收端将包含一个架构(可选)和一个接收管道,其中使用了自定义组件。上述两个属性可以在 BizTalk Server 管理控制台的管道配置中设置。在测试此功能时,将在指定的用于存储大文件的位置创建一个 .msg 文件,并将小型 XML 消息通过 MessageBox
传递。该消息可以由编排或发送管道组件拾取以进行进一步处理。
相关文章
文章 使用 BizTalk 传输大文件 - 发送端 描述了 BizTalk 上的发送端以及如何通过计划任务删除大文件。
文章 使用 Windows 服务和 BizTalk Server 传输超大文件 描述了如何使用 Windows 服务和 BizTalk 传输超大文件(高达 2GB)。