How to encode and to decode a file attachment Visual Basic

Microsoft was nice enough to post a code example in C#, but I’m lazy and haven’t moved away from Visual Basic.

How to encode and to decode a file attachment programmatically by using Visual C# Visual Basic in InfoPath 2010 or in InfoPath 2007

C# example:
http://support.microsoft.com/kb/2517906

VB examples:

decode.vb

Imports System
Imports System.IO
Imports System.Text

Namespace InfoPathAttachmentEncoding
    '''
<summary> ''' Decodes a file attachment and saves it to a specified path.
 ''' </summary>
    Public Class InfoPathAttachmentDecoder
        Private Const SP1Header_Size As Integer = 20
        Private Const FIXED_HEADER As Integer = 16

        Private fileSize As Integer
        Private attachmentNameLength As Integer
        Private attachmentName As String
        Private m_decodedAttachment As Byte()

        '''
<summary> ''' Accepts the Base64 encoded string
 ''' that is the attachment.
 ''' </summary>
        Public Sub New(ByVal theBase64EncodedString As String)
            Dim theData As Byte() = Convert.FromBase64String(theBase64EncodedString)
            Using ms As New MemoryStream(theData)
                Dim theReader As New BinaryReader(ms)
                DecodeAttachment(theReader)
            End Using
        End Sub

        Private Sub DecodeAttachment(ByVal theReader As BinaryReader)
            'Position the reader to obtain the file size.
            Dim headerData As Byte() = New Byte(FIXED_HEADER - 1) {}
            headerData = theReader.ReadBytes(headerData.Length)

            fileSize = CInt(theReader.ReadUInt32())
            attachmentNameLength = CInt(theReader.ReadUInt32()) * 2

            Dim fileNameBytes As Byte() = theReader.ReadBytes(attachmentNameLength)
            'InfoPath uses UTF8 encoding.
            Dim enc As Encoding = Encoding.Unicode
            attachmentName = enc.GetString(fileNameBytes, 0, attachmentNameLength - 2)
            m_decodedAttachment = theReader.ReadBytes(fileSize)
        End Sub

        Public Sub SaveAttachment(ByVal saveLocation As String)
            Dim fullFileName As String = saveLocation
            If Not fullFileName.EndsWith(Path.DirectorySeparatorChar.ToString()) Then
                fullFileName += Path.DirectorySeparatorChar
            End If

            fullFileName += attachmentName

            If File.Exists(fullFileName) Then
                File.Delete(fullFileName)
            End If

            Dim fs As New FileStream(fullFileName, FileMode.CreateNew)
            Dim bw As New BinaryWriter(fs)
            bw.Write(m_decodedAttachment)

            bw.Close()
            fs.Close()
        End Sub

        Public ReadOnly Property Filename() As String
            Get
                Return attachmentName
            End Get
        End Property

        Public ReadOnly Property DecodedAttachment() As Byte()
            Get
                Return m_decodedAttachment
            End Get
        End Property
    End Class
End Namespace

encode.vb

Imports System
Imports System.IO
Imports System.Text
Imports System.Security.Cryptography
Imports InfoPathAttachmentEncoding

Namespace InfoPathAttachmentEncoding
    '''
<summary> ''' InfoPathAttachment encodes file data into the format expected by InfoPath for use in file attachment nodes.
 ''' </summary>
    Public Class InfoPathAttachmentEncoder
        Private base64EncodedFile As String = String.Empty
        Private fullyQualifiedFileName As String

        '''
<summary> ''' Creates an encoder to create an InfoPath attachment string.
 ''' </summary>
        '''
        Public Sub New(ByVal fullyQualifiedFileName As String)
            If fullyQualifiedFileName = String.Empty Then
                Throw New ArgumentException("Must specify file name", "fullyQualifiedFileName")
            End If

            If Not File.Exists(fullyQualifiedFileName) Then
                Throw New FileNotFoundException("File does not exist: " + fullyQualifiedFileName, fullyQualifiedFileName)
            End If

            Me.fullyQualifiedFileName = fullyQualifiedFileName
        End Sub

        '''
<summary> ''' Returns a Base64 encoded string.
 ''' </summary>
        ''' String
        Public Function ToBase64String() As String
            If base64EncodedFile <> String.Empty Then
                Return base64EncodedFile
            End If

            ' This memory stream will hold the InfoPath file attachment buffer before Base64 encoding.
            Dim ms As New MemoryStream()

            ' Obtain the file information.
            Using br As New BinaryReader(File.Open(fullyQualifiedFileName, FileMode.Open, FileAccess.Read, FileShare.Read))
                Dim fileName As String = Path.GetFileName(fullyQualifiedFileName)

                Dim fileNameLength As UInteger = CUInt(fileName.Length) + 1

                Dim fileNameBytes As Byte() = Encoding.Unicode.GetBytes(fileName)

                Using bw As New BinaryWriter(ms)
                    ' Write the InfoPath attachment signature.
                    bw.Write(New Byte() {&HC7, &H49, &H46, &H41})

                    ' Write the default header information.
                    bw.Write(CUInt(&H14))
                    ' size
                    bw.Write(CUInt(&H1))
                    ' version
                    bw.Write(CUInt(&H0))
                    ' reserved
                    ' Write the file size.
                    bw.Write(CUInt(br.BaseStream.Length))

                    ' Write the size of the file name.
                    bw.Write(CUInt(fileNameLength))

                    ' Write the file name (Unicode encoded).
                    bw.Write(fileNameBytes)

                    ' Write the file name terminator. This is two nulls in Unicode.
                    bw.Write(New Byte() {0, 0})

                    ' Iterate through the file reading data and writing it to the outbuffer.
                    Dim data As Byte() = New Byte(64 * 1024 - 1) {}
                    Dim bytesRead As Integer = 1

                    While bytesRead > 0
                        bytesRead = br.Read(data, 0, data.Length)
                        bw.Write(data, 0, bytesRead)
                    End While
                End Using
            End Using

            ' This memorystream will hold the Base64 encoded InfoPath attachment.
            Dim msOut As New MemoryStream()

            Using br As New BinaryReader(New MemoryStream(ms.ToArray()))
                ' Create a Base64 transform to do the encoding.
                Dim tf As New ToBase64Transform()

                Dim data As Byte() = New Byte(tf.InputBlockSize - 1) {}
                Dim outData As Byte() = New Byte(tf.OutputBlockSize - 1) {}

                Dim bytesRead As Integer = 1

                While bytesRead > 0
                    bytesRead = br.Read(data, 0, data.Length)

                    If bytesRead = data.Length Then
                        tf.TransformBlock(data, 0, bytesRead, outData, 0)
                    Else
                        outData = tf.TransformFinalBlock(data, 0, bytesRead)
                    End If

                    msOut.Write(outData, 0, outData.Length)
                End While
            End Using

            msOut.Close()

            Return InlineAssignHelper(base64EncodedFile, Encoding.ASCII.GetString(msOut.ToArray()))
        End Function
        Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, ByVal value As T) As T
            target = value
            Return value
        End Function
    End Class
End Namespace

Imports

Imports InfoPathAttachmentEncoding

btnAttach_Clicked

        Public Sub btnAttach_Clicked(ByVal sender As Object, ByVal e As ClickedEventArgs)

            Dim ns As XmlNamespaceManager = Me.NamespaceManager
            Dim xnMain As XPathNavigator = Me.MainDataSource.CreateNavigator()
            Dim xnAttNode As XPathNavigator = xnMain.SelectSingleNode("/my:myFields/my:theAttachmentField", ns)
            Dim xnFileName As XPathNavigator = xnMain.SelectSingleNode("/my:myFields/my:theAttachmentName", ns)

            'Obtain the text of the filename node.
            Dim fileName As String = xnFileName.Value
            If fileName.Length > 0 Then
                'Encode the file and assign it to the attachment node.
                Dim myEncoder As New InfoPathAttachmentEncoder(fileName)

                'Check for the "xsi:nil" attribute on the file attachment node and remove it
                'before setting the value to attach the filerRemove the "nil" attribute
                If xnAttNode.MoveToAttribute("nil", "http://www.w3.org/2001/XMLSchema-instance") Then
                    xnAttNode.DeleteSelf()
                End If

                'Attach the file
                xnAttNode.SetValue(myEncoder.ToBase64String())
            End If
        End Sub

btnSave_Clicked

        Public Sub btnSave_Clicked(ByVal sender As Object, ByVal e As ClickedEventArgs)
            'Create an XmlNamespaceManager
            Dim ns As XmlNamespaceManager = Me.NamespaceManager

            'Create an XPathNavigator object for the Main data source
            Dim xnMain As XPathNavigator = Me.MainDataSource.CreateNavigator()

            'Create an XPathNavigator object for the attachment node
            Dim xnAttNode As XPathNavigator = xnMain.SelectSingleNode("/my:myFields/my:theAttachmentField", ns)

            'Obtain the text of the node.
            Dim theAttachment As String = xnAttNode.Value
            If theAttachment.Length > 0 Then
                Dim myDecoder As New InfoPathAttachmentDecoder(theAttachment)
                myDecoder.SaveAttachment("----------ENTER THE PATH HERE-----------")
            End If

        End Sub

Leave a Reply

Your email address will not be published. Required fields are marked *