Colabora
 

Serializar y Deserializar imágenes en XML

 

Fecha: 17/Ene/2007 (17 Enero 2007)
Autor: David Sancho

 


Introducción

Esta es una pequeña mejora que he realizado sobre una clase ya publicada en esta página anteriormente por Christian Omar Bigentini. Esta clase se puede ver en el siguiente link .

Por tanto, lo primero agradecer a Christian su colaboración, que supone la base del código que presento, siendo mi aportación una pequeña parte dentro de dicho código.

 

Contenido

En su momento me encontré con la necesidad de transmitir imágenes desde un Servicio Web hacia los clientes y viceversa. Para ello, encontré la clase antes mencionada, que serializa el archivo de imagen en formato XML para enviarlo y lo deserializa en la recepción obteniendo de nuevo la imagen.


Al utilizarla me encontré con un par de necesidades que esta clase no cubría:

- La primera es que la transparencia del fondo de las imágenes se perdía en la conversión.

- La segunda es que me interesaba incluir cierta información básica (en mi caso un nombre que identificaba a la imagen) que se pudiera recuperar en la recepción de la imagen.


Pues bien, estas son las 2 mejoras que he incluido en la clase.

- La transparencia se perdía porque se forzaba a guardar la imagen en el stream con formato Bmp. Así que lo que hice fue guardarla con el formato obtenido de las propiedades de la imagen.

- Para el nombre de la imagen he incluido un método que recibe una cadena de texto como parámetro y la guarda en un atributo llamado Nombre. Al recuperar la imagen, esta cadena se guarda en la propiedad Tag de la imagen para poder acceder a ella.


La clase pues, incluye 3 métodos: 2 para convertir la imagen en un nodo XML, uno de ellos sólo recibe la imagen y el otro recibe también un texto que se podrá recuperar en la recepción; y el método que deserializa el nodo XML recibido y devuelve el objeto imagen.

 

Código

Este es el código de la clase en Visual Basic:

Public Class ConversorImagen

    Public Shared Function ImageToXMLNode(ByVal imagen As Drawing.Image) As XmlNode
        Dim oStream As New System.IO.MemoryStream
        Dim oDom As New XmlDocument
        Dim mResult As New IO.MemoryStream
        Dim LenData As Long = 0
        Dim Buffer() As Byte
        Dim oBinaryReader As IO.BinaryReader
        Dim oXMLTextWriter As XmlTextWriter
        Dim oStreamReader As IO.StreamReader
        Dim StrResult As String

        'Verifico si existe imagen a serializar
        If Not imagen Is Nothing Then

            'Se graba en Stream para almacenar la imagen en formato binario
            'Se conserva el formato de la imagen
            Dim imgF As Drawing.Imaging.ImageFormat
            imgF = imagen.RawFormat
            imagen.Save(oStream, imgF)
            
            oStream.Position = 0

            LenData = oStream.Length - 1

            'Verifico la longitud de datos a serializar        
            If LenData > 0 Then
                ReDim Buffer(Convert.ToInt32(LenData)) 'Genero Buffer

                'Leo los datos binarios
                oBinaryReader = New IO.BinaryReader(oStream, System.Text.Encoding.UTF8)
                oBinaryReader.Read(Buffer, 0, Buffer.Length)

                'Creo XMLTextWriter y agrego nodo con la imagen
                oXMLTextWriter = New XmlTextWriter(mResult, System.Text.Encoding.UTF8)
                oXMLTextWriter.WriteStartDocument()
                oXMLTextWriter.WriteStartElement("BinaryData")
                oXMLTextWriter.WriteBase64(Buffer, 0, Buffer.Length)
                oXMLTextWriter.WriteEndElement()
                oXMLTextWriter.WriteEndDocument()
                oXMLTextWriter.Flush()

                'posiciono en 0 el resultado
                mResult.Position = 0

                'Pasa el Stream a String y retorna
                oStreamReader = New IO.StreamReader(mResult, System.Text.Encoding.UTF8)
                StrResult = oStreamReader.ReadToEnd()
                oStreamReader.Close()

                'Agrego Nuevo Nodo con imagen
                oDom.LoadXml(StrResult)
                Return oDom.DocumentElement
            Else
                'En caso de no existir datos retorno el XML con formato vacio
                oDom.LoadXml("<BinaryData/>")
                Return oDom.DocumentElement.CloneNode(True)
            End If

        Else
            'no hay imagen devuelvo el XML Vacio
            oDom.LoadXml("<BinaryData/>")
            Return oDom.DocumentElement.CloneNode(True)
        End If
    End Function

    Public Shared Function ImageToXMLNode( _
                ByVal imagen As Drawing.Image, _
                ByVal nombre As String) As XmlNode
        Dim oStream As New System.IO.MemoryStream
        Dim oDom As New XmlDocument
        Dim mResult As New IO.MemoryStream
        Dim LenData As Long = 0
        Dim Buffer() As Byte
        Dim oBinaryReader As IO.BinaryReader
        Dim oXMLTextWriter As XmlTextWriter
        Dim oStreamReader As IO.StreamReader
        Dim StrResult As String

        'Verifico si existe imagen a serializar
        If Not imagen Is Nothing Then

            'Se graba en Stream para almacenar la imagen en formato binario
            'Se conserva el formato de la imagen
            Dim imgF As Drawing.Imaging.ImageFormat
            imgF = imagen.RawFormat
            imagen.Save(oStream, imgF)

            oStream.Position = 0

            LenData = oStream.Length - 1

            'Verifico la longitud de datos a serializar        
            If LenData > 0 Then
                ReDim Buffer(Convert.ToInt32(LenData)) 'Genero Buffer

                'Leo los datos binarios
                oBinaryReader = New IO.BinaryReader(oStream, System.Text.Encoding.UTF8)
                oBinaryReader.Read(Buffer, 0, Buffer.Length)

                'Creo XMLTextWriter y agrego nodo con la imagen
                oXMLTextWriter = New XmlTextWriter(mResult, System.Text.Encoding.UTF8)
                oXMLTextWriter.WriteStartDocument()
                oXMLTextWriter.WriteStartElement("BinaryData")
                oXMLTextWriter.WriteAttributeString("Nombre", nombre)
                oXMLTextWriter.WriteBase64(Buffer, 0, Buffer.Length)
                oXMLTextWriter.WriteEndElement()
                oXMLTextWriter.WriteEndDocument()
                oXMLTextWriter.Flush()

                'posiciono en 0 el resultado
                mResult.Position = 0

                'Pasa el Stream a String y retorna
                oStreamReader = New IO.StreamReader(mResult, System.Text.Encoding.UTF8)
                StrResult = oStreamReader.ReadToEnd()
                oStreamReader.Close()

                'Agrego Nuevo Nodo con imagen
                oDom.LoadXml(StrResult)
                Return oDom.DocumentElement
            Else
                'En caso de no existir datos retorno el XML con formato vacio
                oDom.LoadXml("<BinaryData/>")
                Return oDom.DocumentElement.CloneNode(True)
            End If

        Else
            'no hay imagen devuelvo el XML Vacio
            oDom.LoadXml("<BinaryData/>")
            Return oDom.DocumentElement.CloneNode(True)
        End If
    End Function


    'Desserializa XML y retorna la Imágen
    Public Shared Function XMLNodeToImage(ByVal Nodo As Xml.XmlNode) As Image
        Dim IntResult As Integer = 0
        Dim IntPosition As Integer = 0
        Dim LenBytes As Integer = 1024 * 1024 '1024KB - 1MB Lee bloques de 1MB
        Dim myBytes(LenBytes - 1) As Byte
        Dim oMem As New IO.MemoryStream
        Dim oXMLTextReader As Xml.XmlTextReader
        Dim NodeFound As Boolean = False
        'Dim oStreamReader As IO.StreamReader
        Dim oStreamWriter As IO.StreamWriter
        Dim oTempMem As New IO.MemoryStream
        Dim nombre As String = ""

        Try
            'Cargo nodo de texto en Memory Stream
            ' para almacenar la imagen temporalmente en bytes
            oStreamWriter = New IO.StreamWriter(oTempMem, System.Text.Encoding.UTF8)
            oStreamWriter.Write(Nodo.OuterXml)
            oStreamWriter.Flush()
            oTempMem.Position = 0

            'Cargo un xmlReader con el Memory Stream para leer la imágen almacenada
            oXMLTextReader = New Xml.XmlTextReader(oTempMem)

            'Busco el Nodo en Binario
            Do While oXMLTextReader.Read
                If oXMLTextReader.Name = "BinaryData" Then
                    NodeFound = True
                    Exit Do
                End If
            Loop

            'Verifico si se encontró el Nodo con la imagen
            If NodeFound Then

                If oXMLTextReader.HasAttributes Then
                    nombre = oXMLTextReader.GetAttribute("Nombre")
                End If

                'Lo encontro, me muevo a la Posicion Inicial del Stream para leerlo
                IntPosition = 0

                'Intento Leer
                IntResult = oXMLTextReader.ReadBase64(myBytes, 0, LenBytes)
                Do While IntResult > 0

                    'Escribe datos

                    oMem.Write(myBytes, 0, IntResult)

                    'Limpio el array
                    Array.Clear(myBytes, 0, LenBytes)

                    'Leo nuevamente
                    IntResult = oXMLTextReader.ReadBase64(myBytes, 0, LenBytes)

                Loop
                Try
                    'Intento crear la Imagen y retornarla si no devuelvo Nothing
                    Dim img As Image
                    img = Bitmap.FromStream(oMem, True, True)
                    If (nombre IsNot Nothing) AndAlso (nombre.Length > 0) Then
                        img.Tag = nombre
                    End If
                    Return img
                Catch
                    Return Nothing
                End Try
            Else
                'No encontró el nodo de imágen
                Return Nothing
            End If

        Catch ex As Exception
            'Ocurrio un error no contemplado Retorno Nothing    
            Return Nothing
        End Try

    End Function

    
End Class

 

Espero que estas mejoras os sirvan de ayuda. Saludos.

 


Espacios de nombres usados en el código de este artículo:

System.Drawing
System.Xml

 



Ir al índice principal de el Guille