Hi,
I have put together an example function which is VBA and I have used in my VBA applications. It seems to (I haven't done extensive testing) decode infopath attachment fields from an Infopath xml form successfully. I use Infopath 2003 and Microsoft Office 2003 Professional
I don't use this code inside form templates themselves, I use in programs like Microsoft Access - but maybe with this you can adapt to your need.
In Microsoft Access under the Visual Basic editor, I first had to go to Tools menu -> references and tick to add Microsoft XML v2.0 as a reference.
Then make a new code module and paste the code into it in it's entirety
Then use the extractAttachment(strFileName, strXPath, strOutputFolder) function to save a file which is the attachment from the Infopath form specified.
You will need to specify the xPath to the field in strXPath.
I have commented to the code as best I can to show understanding of what it is doing and how it works. I had to deduce the function by reading numerous posts around the net to try and work out a working method - all seemed to be in C or other languages and were outside my scope to use - but finally after much experimenting and inching closer and closer to the answer te below code is made. I learnt about byte arrays and binary file creation as well as getting access to the XML object model through VBA.
I think this will be useful for many. Good luck!
'Infopath file attachment extraction Module
'by Ryan Marshall
'
'compiled from numerous web sources and trial and error to make a functional extraction routine.
'Methods:
' extractAttachment(strFormFileName,strXPath,strOutputFolder)
' strFormFileName = filepath to form to extract attachment from
' strXPath = xPath expression to field which to extract attachment from (single Node only)
' strOutputFolder = folderpath to folder where to save extracted attachment to (with terminating "\")
'Custom data types
'for Byte array to Long (number) conversion
Private Type LongByteType
b1 As Byte
b2 As Byte
b3 As Byte
b4 As Byte
End Type
Private Type LongType
l As Long
End Type
Private LongRec As LongByteType
Private MyLong As LongType
Public Sub extractAttachment(strFileName, strXPath, strOutputFolder)
Dim iFile As Integer 'binary file number index
Dim oNode As IXMLDOMNode 'base64 data node from form field
Dim nodeValue() As Byte 'full byte array of base64 data
Dim fileContent() As Byte 'byte array of file data only
Dim arrFileNameSize(4) As Byte 'byte array of file size only
Dim arrFileName() As Byte 'byte array of filename only
Dim arrFileData() As Byte
Dim oDoc As DOMDocument 'XML document object to load form into
'create XML document in memory
Set oDoc = New DOMDocument
'load form into XML document in memory
If oDoc.Load(strFileName) = True Then
If Not (oDoc Is Nothing) Then
'read attachment node value to oNode object
Set oNode = oDoc.documentElement.selectSingleNode(strXPath)
'typecast the node to base64 as Infopath doesn't do this in node definition
oNode.DataType = "bin.base64"
'convert base64 node value to binary and store in nodeValue byte array
nodeValue = oNode.nodeTypedValue
'read filesize from byte position 20 (4 bytes) to byte array
For i = 20 To 24
arrFileNameSize(i - 20) = nodeValue(i)
Next
'convert filesize byte array to actual file length (and multiply by 2 to get length in bytes)
fileNameSize = ByteArraytoLong(arrFileNameSize) * 2
'now we know how many bytes the filename is read in filename bytes in filename byte array
'(filename starts at 24 and goes for filesize*2 bytes)
ReDim arrFileName(fileNameSize - 1)
For i = 24 To 23 + fileNameSize
arrFileName(i - 24) = nodeValue(i)
Next
'convert filename byte array to filename string (vba takes care of typecasting here)
strFileName = Trim(arrFileName)
'calculate filecontent byte length (equals full byte size of field - header data (default and filename))
headerLength = 24 + fileNameSize
fileDataLength = UBound(nodeValue) - headerLength
'now make room in fileData byte array equal to this size
ReDim arrFileData(fileDataLength)
'store the byte data for the file in the fileData byte array
For i = headerLength To UBound(nodeValue)
arrFileData(i - headerLength) = nodeValue(i)
Next
'open up output binary file
iFile = FreeFile()
strOutputFileName = strOutputFolder & strFileName
Open strOutputFileName For Binary Access Write As iFile
'output entire fileContent byte array to file
Put iFile, , arrFileData
Close iFile
End If
End If
End Sub
'Byte array to Long conversion function using custom Types
Public Function ByteArraytoLong(pArray As Variant) As Long
LongRec.b1 = pArray(0)
LongRec.b2 = pArray(1)
LongRec.b3 = pArray(2)
LongRec.b4 = pArray(3)
LSet MyLong = LongRec
ByteArraytoLong = MyLong.l
End Function