Attach PDF File to an XML File

We're developing an application that needs to use XML File Transfer to move information between multiple locations. Security concerns (not on our end) negate the use of web services so we need to be able to embed a PDF document into an XML file that we send to other parties. We have used a similar approach to upload any type of document into a SQL Server 2000 database but need help working out how to do this using VB.NET and XML files.
pstanfordDirectorAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Melih SARICAOwnerCommented:
ok..

To add a file into an xml ..

first read it into a stream .. and get bytes..

after that convertobase64 string.. and add that string into xml...

and for using it convert.frombase64string to get the byte array

pstanfordDirectorAuthor Commented:
Thanks, I guess I wasn't specific enough in the way I worded the question. I'm comfortable with the streaming and conversion to base64 which I already do when uploading a file and storing it in a database. I was more interested in how to define the element in the XSD, i.e. is there anything specific that needs to be done when defining the XML element that will hold the PDF? For example, if I define a DateTime element as xs:dateTime, a FileName element as xs:string, do I then define the PDF element as xs:base64Binary? Even though we're not going to use web services in the immediate short-term I'm trying to make sure we specify valid XML schema constructs and WSDL methods as though we were using web srvices so any future development does not have to be exntesively redone.
Melih SARICAOwnerCommented:
base64 is a string .. xs:string is enough..

cuz you are processing the xml.. while ur parsing the xml  u ll and extract the pdf file
Rowby Goren Makes an Impact on Screen and Online

Learn about longtime user Rowby Goren and his great contributions to the site. We explore his method for posing questions that are likely to yield a solution, and take a look at how his career transformed from a Hollywood writer to a website entrepreneur.

pstanfordDirectorAuthor Commented:
Sorry for the delay in responding, I've only been able to get back to this issue today. So, I believe I've created a valid XML file and embedded a PDF file inside it but to prove it I'd now like to read the XML file back, extract the data and see if I can display it as a PDF file again. I've included the code used to create the file (VB.NET in an ASP.NET page) and the resulting XML file. When I parse the XML file how specifically do I convert the byte array I wrote back to a PDF file and display it? I've increased the points to 500 because this is now a critical step in the project.

Thanks and best regards
Imports System
Imports System.Xml
Imports System.IO
 
Namespace WriteXML
 
 
Public Class _Default
    Inherits System.Web.UI.Page
        Public AttachedFileName As String
        Public AttachedFileType As String
        Public AttachedFileSize As Long
        Public AttachedFileTimeStamp As String
        Protected WithEvents txtXMLOutput As System.Web.UI.WebControls.TextBox
        Protected WithEvents btnReadXML As System.Web.UI.WebControls.Button
 
#Region " Web Form Designer Generated Code "
 
    'This call is required by the Web Form Designer.
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
 
        End Sub
    Protected WithEvents Label1 As System.Web.UI.WebControls.Label
        Protected WithEvents btnCreateXML As System.Web.UI.WebControls.Button
        Protected WithEvents TextBox2 As System.Web.UI.WebControls.TextBox
        Protected WithEvents Label2 As System.Web.UI.WebControls.Label
        Protected WithEvents Label3 As System.Web.UI.WebControls.Label
        Protected WithEvents txtAttachmentFileName As System.Web.UI.WebControls.TextBox
        Protected WithEvents txtXMLFileName As System.Web.UI.WebControls.TextBox
 
    'NOTE: The following placeholder declaration is required by the Web Form Designer.
    'Do not delete or move it.
    Private designerPlaceholderDeclaration As System.Object
 
    Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
        'CODEGEN: This method call is required by the Web Form Designer
        'Do not modify it using the code editor.
        InitializeComponent()
    End Sub
 
#End Region
 
    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'Put user code to initialize the page here
    End Sub
        '-----------------------------------------------------------------------------------
        ' In a production environment you would add appropriate error handling to ensure
        ' that the file you wanted to embed in the XML file existed and that it had a 
        ' length greater than zero.
        '
        ' Step 1:   Get the file attributes including:
        '           File Name
        '           File Extension
        '           File Size
        '           File TimeStamp (date last updated)
        '
        ' Step 2:   Create the XML file by creating a new XMLTextWriter instance then:
        '           Set the Indentation properties for the XML file
        '           Create the XML Header <?xml version="1.0" ?> 
        '           Write a comment to explain what the XML file contains
        '           Write a Start Element tag, e.g. <FIMSJobNumber>
        '           Write the data corresponding to that element, e.g. AJ12345678
        '           Write an End Element tag, e.g. </FIMSJobNumber>
        '
        ' NOTE:     In a production environment this would be achieved through a 
        '           parameterised function where the Start Element, Data and End Element
        '           were written for each element in the parameter list
        '
        'Step 3:    Including a file attachment in the XML document requires the file to be
        '           converted to a byte array before being encoded. See the following code:
        '
        '               .WriteStartElement("StatementContents")
        '               .WriteCData(Convert.ToBase64String(GetByteArrayFromFileField(txtAttachmentFileName.Text), 0, AttachedFileSize))
        '               .WriteEndElement()
        '
        ' Step 4:   Flush the XML Stream and Close it to write the XML file
        '-----------------------------------------------------------------------------------
        Private Sub btnCreateXML_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCreateXML.Click
            GetFileAttributes(txtAttachmentFileName.Text.ToString)
            Dim writer As New XmlTextWriter(txtXMLFileName.Text.ToString, Nothing)
 
            With writer
                .Formatting = Formatting.Indented
                .Indentation = 4
                .WriteStartDocument()
                .WriteComment("This XML example has been written by NSWPF for DAL for use within FIMS")
                .WriteStartElement("AnalysisResult")
                .WriteStartElement("FIMSJobNumber")
                .WriteString("AJ12345678")
                .WriteEndElement()
                .WriteStartElement("PoliceReferenceNumber")
                .WriteString("ABC12345")
                .WriteEndElement()
                .WriteStartElement("TransactionDateTime")
                .WriteString("19/11/2009")
                .WriteEndElement()
                .WriteStartElement("ExhibitDefinition")
                .WriteStartElement("ExhibitNumber")
                .WriteString("EX9876543210")
                .WriteEndElement()
                .WriteStartElement("ExhibitDescription")
                .WriteString("Blue polyester shirt")
                .WriteEndElement()
                .WriteStartElement("ParentExhibitNumber")
                .WriteString("EX9876543202")
                .WriteEndElement()
                .WriteEndElement()
                .WriteStartElement("DestroyedDate")
                .WriteString("14/11/2009")
                .WriteEndElement()
                .WriteStartElement("DNAAnalysisResult")
                .WriteStartElement("ResultDateTime")
                .WriteString("15/11/2009")
                .WriteEndElement()
                .WriteStartElement("DNAProfileResult")
                .WriteStartElement("ProfileRecoveredInd")
                .WriteString("Single")
                .WriteEndElement()
                .WriteStartElement("ProfileNumber")
                .WriteString("DAL12345")
                .WriteEndElement()
                .WriteStartElement("ProfileGender")
                .WriteString("Female")
                .WriteEndElement()
                .WriteStartElement("IntendedDNAIndex")
                .WriteString("None")
                .WriteEndElement()
                .WriteStartElement("isProfileMatchesVictim")
                .WriteString("True")
                .WriteEndElement()
                .WriteStartElement("OccurrenceInPopulation")
                .WriteString("1000")
                .WriteEndElement()
                .WriteEndElement()
                .WriteStartElement("ExhibitExaminationArea")
                .WriteString("Shirt Pocket")
                .WriteEndElement()
                .WriteStartElement("ExhibitNotTestedReason")
                .WriteString("")
                .WriteEndElement()
                .WriteStartElement("BioMaterialDetails")
                .WriteStartElement("MaterialTestedFor")
                .WriteString("Blood")
                .WriteEndElement()
                .WriteStartElement("isMaterialFound")
                .WriteString("True")
                .WriteEndElement()
                .WriteEndElement()
                .WriteStartElement("AnalystsDetailsType")
                .WriteStartElement("Name")
                .WriteString("Paul Abernathy")
                .WriteEndElement()
                .WriteStartElement("Qualifications")
                .WriteString("MSc")
                .WriteEndElement()
                .WriteEndElement()
                .WriteStartElement("DNALinkResult")
                .WriteString("Appropriate details will be returned here later")
                .WriteEndElement()
                .WriteStartElement("ResultComments")
                .WriteString("This might contain contents about the nature of the testing performed etc. but will not necessarily be reflected in the statement")
                .WriteEndElement()
                .WriteStartElement("StatementDetails")
                .WriteStartElement("StatementDateTime")
                .WriteString("15/11/2009")
                .WriteEndElement()
                .WriteStartElement("StatementFileName")
                .WriteString(AttachedFileName)
                .WriteEndElement()
                .WriteStartElement("StatementFileType")
                .WriteString(AttachedFileType)
                .WriteEndElement()
                .WriteStartElement("StatementFileTimeStamp")
                .WriteString(AttachedFileTimeStamp)
                .WriteEndElement()
                .WriteStartElement("StatementFileSize")
                .WriteString(AttachedFileSize)
                .WriteEndElement()
                .WriteStartElement("StatementContents")
                .WriteCData(Convert.ToBase64String(GetByteArrayFromFileField(txtAttachmentFileName.Text), 0, AttachedFileSize))
                .WriteEndElement()
                .WriteEndElement()
                .WriteEndElement()
                .WriteEndDocument()
                .Flush()
                .Close()
            End With
        End Sub
        '-----------------------------------------------------------------------------------
        ' This function takes input from a standard textbox and returns a byte array which 
        ' can be written to an output file or attached to an XML file
        '-----------------------------------------------------------------------------------
        Private Function GetByteArrayFromFileField(ByVal FileName As String) As Byte()
            Dim bytData() As Byte
            Dim objStream As System.IO.Stream
            If FileFieldSelected(FileName) Then
                ReDim bytData(AttachedFileSize)
                objStream = New FileStream(FileName, FileMode.Open, FileAccess.Read)
                objStream.Read(bytData, 0, AttachedFileSize)
                Return bytData
            End If
        End Function
        '-----------------------------------------------------------------------------------
        ' This function takes input from a standard textbox and determines whether or not
        ' the file exists
        '-----------------------------------------------------------------------------------
        Private Function FileFieldSelected(ByVal FileField As String) As Boolean
            If FileField Is Nothing Then
                Return False
            End If
            If FileField.Length = 0 Then
                Return False
            End If
            Return True
        End Function
        '-----------------------------------------------------------------------------------
        ' Read the attributes of the file being attached to the XML document so its
        ' meta-data can be included in the output.
        '
        ' NOTE: The file attributes have been declared as public variables for the sake of
        ' simplicity only
        '-----------------------------------------------------------------------------------
        Private Sub GetFileAttributes(ByVal MyFilePath As String)
            Dim MyFile As New FileInfo(MyFilePath)
            AttachedFileSize = MyFile.Length
            AttachedFileType = MyFile.Extension
            AttachedFileName = MyFile.Name
            AttachedFileTimeStamp = MyFile.LastWriteTime
        End Sub
 
        Private Sub btnReadXML_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnReadXML.Click
            ' Read the file back in and parse to ensure well formed XML.
            Dim doc As New XmlDocument
            ' Preserve white space for readability.
            doc.PreserveWhitespace = True
            ' Load the file.
            doc.Load(txtXMLFileName.Text)
 
            ' Write the XML content to the console.
            'Console.Write(doc.InnerXml)
            txtXMLOutput.Text = doc.InnerXml
        End Sub
    End Class
End Namespace

Open in new window

Test2.XML
Melih SARICAOwnerCommented:
when u read Cdata from  StatementContents node..

what u ll get is an string of base64


then

with StatementContents with convert.Frombase64String

sample
----------
   Dim binaryData() As Byte
   Try
      binaryData = System.Convert.FromBase64String(base64String)
   Catch exp As System.ArgumentNullException
      System.Console.WriteLine("Base 64 string is null.")
      Return
   Catch exp As System.FormatException
      System.Console.WriteLine("Base 64 length is not 4 or is not an even multiple of 4.")
      Return
 End Try

pstanfordDirectorAuthor Commented:
Hi again,

First, thanks for your help so far. I've attached some code which is focussed solely on getting the data back from the StatementContents element of the XML file. What I really need to know is how to take this binary data and save the file (in this case a PDF document) assuming that the attached code will indeed return the true contents of the document. In other words, assuming that I am reading the XML file and converting my base64 data back to binary, how do I now save this to a file?
Dim binaryData As Byte()
 
            Dim reader As New XmlTextReader(txtXMLFileName.Text)
            While reader.Read()
                Select Case reader.NodeType
                    Case XmlNodeType.Element
                        If reader.Name = "StatementContents" Then
                            Try
                                binaryData = System.Convert.FromBase64String(XmlNodeType.Element)
 
                            Catch ex As Exception
 
                            End Try
                        End If
                End Select
            End While

Open in new window

Melih SARICAOwnerCommented:
ok
that is the simplestway

dim _stream as new system.io.filestream("filename and path ",FileMode.Create)
_stream.write(binarydata,0,binarydata.length)
_stream.close()
pstanfordDirectorAuthor Commented:
OK, I've added the code (see attached) but the textbox where I set the StatementFileName doesn't display anything (the page does do a postback) and the APD.PDF document does not get saved to the nominated location, i.e. C:\APD.PDF.

I can't debug the code to see whether anything's actually happening in this procedure. Periodically the debugger just doesn't work with this version of VS.NET 2003 but is there anything obviously wrong with the code?
            Dim binaryData As Byte()
 
            Dim reader As New XmlTextReader(txtXMLFileName.Text)
            While reader.Read()
                Select Case reader.NodeType
                    Case XmlNodeType.Element
                        If reader.Name = "StatementFileName " Then
                            txtFileName.Text = reader.ReadString
                        End If
                        If reader.Name = "StatementContents" Then
                            Try
                                binaryData = System.Convert.FromBase64String(XmlNodeType.Element)
 
                                Dim oStream As New System.io.FileStream("C:\APD.PDF", FileMode.Create)
                                oStream.Write(binaryData, 0, binaryData.Length)
                                oStream.Close()
                            Catch ex As Exception
 
                            End Try
                        End If
                End Select
            End While

Open in new window

Melih SARICAOwnerCommented:
System.Convert.FromBase64String(XmlNodeType.Element)
xmlnodetype.Element is not a value...

reader has an ReadBase64 sub.... u can use it..

detail link

http://msdn.microsoft.com/en-us/library/system.xml.xmltextreader.readbase64%28VS.71%29.aspx
pstanfordDirectorAuthor Commented:
I'm clearly not understanding something here!

Here's the code based on the link you provided (see attached) and here's the error I receive (see attached). The error occurs on the line "base64len = reader.ReadBase64(bytData, 0, AttachedFileSize)"
AttachedFileSize = 45696
            Dim base64len As Long = 0
            ReDim bytData(AttachedFileSize)
 
            Do
                base64len = reader.ReadBase64(bytData, 0, AttachedFileSize)
                'base64len = reader.ReadBase64(bytData, 0, 50)
                If txtFileSize.Text.Length = 0 Then
                    txtFileSize.Text = CStr(base64len)
                End If
                For i = 0 To base64len - 1
                    Console.Write(bytData(i))
                Next i
            Loop While (reader.Name = "StatementContents")

Open in new window

XML-Error.JPG
Melih SARICAOwnerCommented:
ok..

U Add data as cdata.. so first u must read it as cdata and then u must convert it to binary

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
pstanfordDirectorAuthor Commented:
I've had to move on to other things so will accept the solution as offered. I would have preferred a more detailed explanation but it's close enough for my purposes
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Visual Basic.NET

From novice to tech pro — start learning today.