Sending Multiple Zipped Files
In order to
accommodate the scenario where a single ZIP file will contain multiple
ZIP entries, you need to make some modifications to the preceding
example. Specifically, you need to pass in multiple documents and have
them all included within the outbound ZIP file. For this, you can write a
BizTalk Assembler component. The Assembler component will take an array
of messages, loop through them, and compress each of them into the
final message output. To actually send the messages to the assembler,
you can use the ExecuteSendPipeline from an orchestration feature that
is included with BizTalk 2006. What you need to do is build an
orchestration that will have a subscription for all messages required to
be included within the ZIP file. The orchestration listens for these
messages, and when it receives them, it adds them to an ArrayList object. At some point, the orchestration will need to call the send pipeline and pass it in the ArrayList
it has built. From here, the send pipeline will call the Assembler
component, which will add each of the messages it has received within
the ArrayList to the outbound message.
An example of this pattern is included in the SDK with BizTalk under
the \Program Files\Microsoft BizTalk Server 2006\
SDK\Samples\Pipelines\Aggregator\ directory.
Receiving Zipped Files
The reverse scenario
to sending ZIP files is receiving them. When receiving ZIP files, you
will need to create a Decoding component, which can extract the files
inside and submit them to the pipeline for further processing. This
example only addresses the simple example of a ZIP file containing one
XML file inside. The following example could be expanded upon to handle
ZIP files with multiple files inside and files of binary types.
Imports Microsoft.VisualBasic
Imports System
Imports System.ComponentModel
Imports System.Collections
Imports System.Diagnostics
Imports System.Drawing
Imports System.IO
Imports System.Reflection
Imports Microsoft.BizTalk.Component.Interop
Imports Microsoft.Utility.PipelinePropertyAttribute
Imports ICSharpCode.SharpZipLib.Zip
Namespace Microsoft.Utility.PipelineZip
<ComponentCategory(CategoryTypes.CATID_PipelineComponent),_
ComponentCategory(CategoryTypes.CATID_Decoder),_
System.Runtime.InteropServices.Guid("67C8CFB9-D89A-4415-A112-76187FC294D1")> _
Public Class ZipDecodeComponent
Inherits BaseCustomTypeDescriptor
Implements IBaseComponent,_
Microsoft.BizTalk.Component.Interop.IComponent,_
Microsoft.BizTalk.Component.Interop.IPersistPropertyBag, IComponentUI
' Component information
#Region "IBaseComponent"
<Browsable(False)> _
Public ReadOnly Property Name() As String
Get
Return "ZIP decoder"
End Get
End Property
<Browsable(False)> _
Public ReadOnly Property Version() As String
Get
Return "1.0"
End Get
End Property
<Browsable(False)>
Public ReadOnly Property Description() As String
Get
Return "Zip Decode Pipeline Component"
End Get
End Property
<Browsable(False)>
Public ReadOnly Property Icon() As System.IntPtr
Get
Return (CType(resourceManager.GetObject("IconBitmap"),_
Bitmap)).GetHicon()
End Get
End Property
#End Region
Private resourceManager As System.Resources.ResourceManager = New_
System.Resources.ResourceManager_
("Microsoft.Utility.PipelineGnuPG.ZipDecodeComponent",_
System.Reflection.Assembly.GetExecutingAssembly())
' Property: Password
Private _password As String
Public Property Password() As String
Get
Return _password
End Get
Set
_password = Value
End Set
End Property
Private Function Decode(ByVal inStream As Stream) As Stream
Dim inFile As String = Path.GetTempFileName()
Dim outFile As String = Path.ChangeExtension(inFile, "txt")
Try
Dim zipStream As ZipInputStream = New_
ZipInputStream(inStream)
' get password, if supplied
If (Not _password Is Nothing) AndAlso (_password <> "")_
Then
zipStream.Password = _password
End If
' this algorithm demands that the ZIP archive contain
' exactly one file
Dim entry As ZipEntry = zipStream.GetNextEntry()
If entry Is Nothing Then
Throw New ApplicationException("Input ZIP archive does not contain any _
files - expecting exactly one file")
End If
If entry.IsDirectory Then
Throw New ApplicationException("Input ZIP _
contains a directory - expecting exactly one file")
End If
' copy the compressed stream into the output stream
outStream = New MemoryStream()
Dim buffer As Byte() = New Byte(4095){}
Dim count As Integer = 0
count = zipStream.Read(buffer, 0, buffer.Length
Do While count <> 0
outStream.Write(buffer, 0, count)
count = zipStream.Read(buffer, 0, buffer.Length
Loop
' make sure that was the one and only file
entry = zipStream.GetNextEntry()
If Not entry Is Nothing Then
Throw New ApplicationException("Input ZIP archive contains multiple files_
and/or directories - expecting exactly one file")
End If
zipStream.Close()
#If DEBUG Then
outStream.Seek(0, SeekOrigin.Begin)
PipelinePropertyAttribute.FileStreamReadWrite.DumpStreamToFile(outStream,_
outFile)
#End If
outStream.Seek(0, SeekOrigin.Begin)
Catch ex As Exception
System.Diagnostics.Debug.WriteLine(ex)
Throw ex
Finally
If File.Exists(inFile) Then
File.Delete(inFile)
End If
If File.Exists(outFile) Then
File.Delete(outFile)
End If
End Try
Return outStream
End Function
#Region "IPersistPropertyBag Members"
Public Sub InitNew()
End Sub
Public Sub GetClassID(<System.Runtime.InteropServices.Out()> ByRef_
classID As Guid)
classID = New Guid ("19800584-283D-44da-B1EE-0968387DA088")
End Sub
Public Sub Load(ByVal propertyBag As IPropertyBag, ByVal errorLog As_
Integer)
Dim text As String
text = CStr(PropertyBagReadWrite.ReadPropertyBag(propertyBag,_
"Password"))
If Not text Is Nothing Then
_password = text
End If
End Sub
Public Sub Save(ByVal propertyBag As IPropertyBag, _
ByVal clearDirty As Boolean, ByVal saveAllProperties As Boolean)
Dim val As Object
val = CObj(_password)
PropertyBagReadWrite.WritePropertyBag(propertyBag, "Password",_
val)
End Sub
#End Region
#Region "IComponent Members"
Public Function Execute(ByVal pContext As IPipelineContext, _
ByVal pInMsg As Microsoft.BizTalk.Message.Interop.IBaseMessage) As_
Microsoft.BizTalk.Message.Interop.IBaseMessage
Try
If Not pInMsg Is Nothing Then
Dim originalStream As _
Stream = pInMsg.BodyPart.GetOriginalDataStream()
pInMsg.BodyPart.Data = Decode(originalStream)
pContext.ResourceTracker.AddResource_
(pInMsg.BodyPart.Data)
End If
Catch ex As Exception
System.Diagnostics.Debug.WriteLine("Exception caught in_
ZipDecodeComponent::Execute: " & ex.Message)
Throw New ApplicationException("ZipDecodeComponent was_
unable to decompress input stream. This may occur if there is more than_
one file in the ZIP archive. See inner exception for more information.", ex)
End Try
Return pInMsg
End Function
#End Region
#Region "IComponentUI Members"
'<summary>
'The Validate method is called by the BizTalk Editor_
'during the build of a BizTalk project.
'</summary>
'<param name="obj">An Object containing the configuration
'properties.</param>
'<returns>The IEnumerator enables the caller to _enumerate through a
'collection of strings containing error messages. These error_
'messages appear as compiler error messages. To report successful
'property validation, the method should return an empty
'enumerator.</returns>
Public Function Validate(ByVal projectSystem As Object) As_
IEnumerator
' example implementation:
' ArrayList errorList = new ArrayList();
' errorList.Add("This is a compiler error");
' return errorList.GetEnumerator();
Return Nothing
End Function
#End Region
End Class
End Namespace