Web Services Comprimidos

La idea de este artículo es mostrar como con Flex podemos utilizar WebServices y a su vez comprimir lo que el webservice nos devuelve.

Todos sabemos que una de las principales ventajas que RemoteObject tiene ante WebServices es que al ser un protocolo binario, la información que viaja desde el backend a Flex es mucho menor. Debido a esto empezamos a investigar la posibilidad de “reducir” esa cantidad de información.

Esta claro que la mejor forma de “reducir” la información es comprimiéndola, así que encaramos la investigación por ese lado.

Primero empezamos por Flex y nos llevamos la grata sorpresa que la clase ByteArray tiene el metodo “compress” que realiza la compresión del bytearray utilizando zLib. Como personalmente en .Net tengo experiencia en haber utilizado zLib, nos decidimos por esta opción.
En resumen, los que les voy a mostrar es como comprimir el “response” de un webservice utilizando zlib en el backend (en este caso .NET) y luego descomprimirlo en Flex.

BackEnd (.net)

Primero veamos como comprimir con zLib en .Net.
Para esto lo primero que tenemos que hacer es bajarnos el archivo zlib.dll y copiarlo al system32 de nuestra carpeta Windows o bien al Bin de nuestro proyecto .Net.

Luego de esto debemos escribir una clase que nos ayude a realizar la compresión, puede ser:

[ftf w=”450″ h=”200″]

Option Explicit On
Option Strict Off
Option Compare Binary

Imports System
Imports System.IO
Imports System.Web
Imports Microsoft.VisualBasic

Public Class zLib
Public Enum ZCompressLevels
Z_NO_COMPRESSION = 0
Z_BEST_SPEED = 1
Z_BEST_COMPRESSION = 9
Z_DEFAULT_COMPRESSION = (-1)
End Enum

Private Const Z_NULL As Integer = 0
Private Const Z_PARTIAL_FLUSH As Integer = 1
Private Const Z_SYNC_FLUSH As Integer = 2
Private Const Z_FULL_FLUSH As Integer = 3
Private Const Z_FINISH As Integer = 4

Private Const Z_OK As Integer = 0
Private Const Z_STREAM_END As Integer = 1
Private Const Z_NEED_DICT As Integer = 2
Private Const Z_ERRNO As Integer = (-1)
Private Const Z_STREAM_ERROR As Integer = (-2)
Private Const Z_DATA_ERROR As Integer = (-3)
Private Const Z_MEM_ERROR As Integer = (-4)
Private Const Z_BUF_ERROR As Integer = (-5)
Private Const Z_VERSION_ERROR As Integer = (-6)
Private Const Z_PROCESSING_ERROR As Integer = 99

Private Const Z_FILTERED As Integer = 1
Private Const Z_HUFFMAN_ONLY As Integer = 2
Private Const Z_DEFAULT_STRATEGY As Integer = 0

Private Const Z_BINARY As Integer = 0
Private Const Z_ASCII As Integer = 1
Private Const Z_UNKNOWN As Integer = 2

Private Const Z_DEFLATED As Integer = 8

Private Declare Function Compress Lib “ZLIB.DLL” Alias “compress2” (ByRef DestinationArray As Byte, ByRef DestLen As Integer, ByRef SourceArray As Byte, ByVal SourceLen As Integer, ByVal CompressionLevel As Integer) As Integer

Public Function ZCompressByteArray(ByRef ArrayToCompress() As Byte, _
ByRef Return_Array() As Byte, _
Optional ByVal CompressionLevel As ZCompressLevels = ZCompressLevels.Z_BEST_COMPRESSION, _
Optional ByVal TagOriginalSize As Boolean = True, _
Optional ByRef Return_ErrorNum As Integer = 0, _
Optional ByRef Return_ErrorSrc As String = “”, _
Optional ByRef Return_ErrorDesc As String = “”) As Boolean
On Error GoTo ErrorTrap

Dim OrigSize As String
Dim ArrayLenS As Integer
Dim ArrayLenD As Integer
Dim CharCount As Integer
Dim MyCounter As Integer

Erase Return_Array
Return_ErrorNum = 0
Return_ErrorSrc = “”
Return_ErrorDesc = “”

Select Case CompressionLevel
Case ZCompressLevels.Z_BEST_COMPRESSION, ZCompressLevels.Z_BEST_SPEED, ZCompressLevels.Z_DEFAULT_COMPRESSION, ZCompressLevels.Z_NO_COMPRESSION
‘DO NOTHING
Case Else
CompressionLevel = ZCompressLevels.Z_BEST_COMPRESSION
End Select

ArrayLenS = UBound(ArrayToCompress) + 1
If ArrayLenS = 0 Then
ZCompressByteArray = True
Exit Function
End If

ArrayLenD = ArrayLenS + ((ArrayLenS * 0.001) + 15) ‘ Extra 3 bytes on the buffer avoids errors
ReDim Return_Array(ArrayLenD) ‘As Byte

Return_ErrorNum = Compress(Return_Array(0), ArrayLenD, ArrayToCompress(0), ArrayLenS, CompressionLevel)
If Return_ErrorNum <> Z_OK Then
Call Err.Raise(Return_ErrorNum, “ZCompressByteArray >> ZLIB.Compress()”, GetErrorDescription(Return_ErrorNum))
End If

If TagOriginalSize = False Then
ReDim Preserve Return_Array(ArrayLenD – 1)
Else
If ArrayLenS > 2147483647 Then
ReDim Preserve Return_Array(ArrayLenD – 1)
Exit Function
End If

OrigSize = CStr(ArrayLenS)
OrigSize = OrigSize & New String(vbNullChar, 11 – Len(OrigSize))
OrigSize = New String(vbNullChar, 5) & OrigSize

ReDim Preserve Return_Array(ArrayLenD + 15)

For MyCounter = ArrayLenD To ArrayLenD + 15
CharCount = CharCount + 1
Return_Array(MyCounter) = Asc(Mid(OrigSize, CharCount, 1))
Next
End If

ZCompressByteArray = True
Exit Function

ErrorTrap:

Return_ErrorNum = Err.Number
Return_ErrorSrc = Err.Source
Return_ErrorDesc = Err.Description
Err.Clear()
Return False

End Function

Public Function GetErrorDescription(ByVal lngErrorCode As Long) As String

Select Case lngErrorCode
Case Z_OK : GetErrorDescription = “” ‘ No Error
Case Z_STREAM_END : GetErrorDescription = “Data stream reached the end of the stream”
Case Z_NEED_DICT : GetErrorDescription = “A preset dictionary is needed”
Case Z_ERRNO : GetErrorDescription = “A file system error has occured”
Case Z_STREAM_ERROR : GetErrorDescription = “A function parameter is invalid *OR* The stream state was inconsistent”
Case Z_DATA_ERROR : GetErrorDescription = “Input data was corrupted”
Case Z_MEM_ERROR : GetErrorDescription = “Not enough memory”
Case Z_BUF_ERROR : GetErrorDescription = “Not enough room in the output buffer”
Case Z_VERSION_ERROR : GetErrorDescription = “The zlib library version is incompatible with the version assumed by the caller”
Case Z_PROCESSING_ERROR : GetErrorDescription = “A processing error occured”
Case Else : GetErrorDescription = “An unknown error occured”
End Select

End Function
End Class

[/ftf]

Un vez que tenemos esto, en el WebServices debemos comprimir los datos antes de devolverlos. Para esto debemos tener en cuenta dos factores.
1. Para la codificación del string utilizaremos UTF8 en ambos extremos.
2- Los datos deben ir en formato Base64. De esta manera no tendremos problemas con caracteres no imprimibles en SOAP.
Entonces, un WebServcie de ejemplo quedaría así:

[ftf w=”450″ h=”200″]
_
Public Function GetData(ByVal pParam1 As String) As String

Dim cData As String = GenerateData(pParam1) ‘Este metod deberia generar los datos que necesitamos devolver

Dim cDataCompress As String = Compress(cData)

Return cDataCompress

End Function

Private Function Compress(ByVal pData As String) As String

Dim cCompressData As String

Dim BufferData As Byte() = System.Text.Encoding.UTF8.GetBytes(pData)
Dim Buffer As Byte()
Dim ozLib As New zlib
ozLib.ZCompressByteArray(BufferData, Buffer)
cCompressData = Convert.ToBase64String(Buffer, Base64FormattingOptions.None)

Return cCompressData

End Function

[/ftf]

Compresión en Flex

Ya tenemos los datos comprimidos en el backend, ahora veamos como descomprimirlo cuando llegan a nuestra aplicación Flex.
Al igual que en el paso anterior, hagamos una pequeña clase AS que nos facilite el uso de la descompresión.
Esta quedaría:

[ftf w=”450″ h=”200″]

import flash.utils.ByteArray;
import mx.utils.Base64Encoder;
import mx.utils.Base64Decoder;

public class CompressUtil
{
public static function uncompress(str:String):String
{

var oDecoder:Base64Decoder = new Base64Decoder();
oDecoder.decode(str);
var decode:ByteArray = oDecoder.flush();
decode.uncompress();
return decode.toString();

}
}

[/ftf]

De esta manera, antes de utilizar los datos que provienen de nuestro WebService, nos quedaría descomprimirlos de la siguiente manera:

[ftf w=”450″ h=”200″]

/**
* Método que se ejecuta cuando el WebService retorna un valor
*
* @param evt
*
*/
private function wsResult(evt:ResultEvent):void
{

var cResult:String = String(evt.result);

var cData:String = CompressUtil.uncompress(cResult);

//
// aqui ya podemos acceder a nuestros datos mediante la variable cData
//

}

[/ftf]

Conclusión

Para concluir puedo comentarles que actualmente utilizo este método en dos proyectos grandes en los que trabajo y me dio muy buenos resultados, reduciendo significativamente el ancho de banda utilizado a cambio de un pequeño incremento tanto en el Cliente como en el BackEnd para realizar las tareas de compresión-descompresión.
Espero que les sea de interés y lo puedan implementar.
Suerte!!!

7 Comentarios

  1. katratxo

    Existe la clase FZip que …permite cargar un archivo ZIP estándar y extraer el contenido cuando aún el archivo se está cargado…

    Tal vez sirve de algo el comentario… 😉

    Un saludo,

    ./katratxo

  2. Txus

    Y que tal si confiamos en el browser, es decir, que se devuelva un stream en gzip que el browser puede descomprimir on the fly. Mucho más transparente que esta solución que me parece demasiado intrusiva.

  3. Fabián Brussa

    Txus, claro que se puede activar gZip. Aunque para activar esta compresión en un servidor Internet Information Service (ya que hablamos de .NET), hay que tener control sobre el servidor, y en muchos casos esto no ocurre. Me refiero a que a menudo “alquilas” o “contratas” un servicio de hosting en el cual no te permiten realizar esos cambios. En este caso creo que ésta es la única solución. Me parece útil para hacerlo independiente del ambiente donde instales la aplicación.

  4. Txus

    Perdón, uno está acostumbrado a entornos controlables o al menos a poder pedirle a los administradores que realicen ciertas configuraciones, pero tienes razón, no siempre va a ser así.

  5. Sebastian

    Una Aclaracion al respecto de esta entrada que funciona correctamente en Windows 32bits.
    La Libreria zlib esta compilada en 32 bits. por lo que si se intenta correr en un Windows Servidor o No que sea 64bits esta dll provocara un error interno al querer descomprimir los datos, lo que causara que no se descompriman o compriman los datos.
    La Solucion a esto es ir al IIS 7, en Grupo de Aplicaciones. Presionar Boton Derecho y seleccionar “Establecer Valores Predeterminados de Grupos de Aplicaciones”. En la 1º Opcion poner como True el valor para habilitar Compatibilidad con aplicaciones de 32 bits. Con esto el ejemplo funciona perfectamente en Sistemas Operativos de 64 bits.

  6. Humberto

    Buenas Dias… tengo un hosting compartido en godaddy.com y cuando ejecuto la compresion me da este error “System.Security.Permissions.SecurityPermission” sabras de alguna manera para evitarlo? he estado leyendo y parece ser que por ser middle trust explota… 🙁

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Acerca de Made In Flex

Made In Flex es una comunidad de desarrolladores de Apache Flex creada en 2006.

Apache Flex, anteriormente conocido como Adobe Flex, es un SDK (kit de desarrollo de software) para crear aplicaciones enriquecidas - multiplataforma basadas en Adobe Flash donado por Adobe a la fundación Apache in 2011 y promocionado a proyecto de primer nivel en Diciembre de 2012.

Actualmente estamos cambiando muchos aspectos del sitio web para ofrecer un sitio útil para toda la comunidad que tenga en cuenta las necesidades actuales.

Últimas Fotos

Instalador de Apache Flex

Entrar o Registrase