Inicio > ASP.Net, Visual Basic .NET > Imagen thumbnail sin archivo temporal en ASP.NET

Imagen thumbnail sin archivo temporal en ASP.NET

Hoy vamos a sacudir el polvo de este abandonado esporádicamente actualizado blog con un truquillo bastante interesante a la hora de mostrar una versión miniatura de una imagen en nuestra web.

Lo que en general hacíamos es crear la versión miniatura en memoria que indefectiblemente debe existir físicamente en disco para luego enlazarla con nuestro control <img> ó <asp:image>.

El tip consiste en en enlazar nuestro control de imagen contra un archivo del tipo “Generic Handler” (extensión “.ashx”). Básicamente nos permite responder una petición HTTP con un archivo distinto a la típica página conformada por HTML y en este caso, por supuesto, vamos a utilizarlo para responder la petición con nuestra imagen miniatura.

A mi parecer hay 2 problemas que evitamos con esto:

  • Problemas de permisos sobre el subdirectorio y/o archivos temporales que creamos ya que estamos hablando del contexto web.
  • También debemos buscar la forma de eliminar dicho archivo temporal en algún momento para que no se amontonen en cantidades astronómicas.

Antes ACLARO:

  • En este ejemplo no se encripta la ruta de la imagen pasada como argumento al archivo ashx. Esto debería hacerse, ya que en el render HTML enviado al cliente se observa dicha ruta completa con un simple “View Source” en nuestro navegador favorito.

  • Debemos agregar la referencia a System.Windows.Forms. Más adelante, en el código, verán que utilizo el método TextRenderer.DrawText para dibujar un string dentro de la imagen indicando que ha habido un error.

Ahora sí. Vamos a escribir una pagina de ejemplo donde se muestra 2 versiones de una misma imagen.
La primera (izquierda) muestra la imagen con sus dimensiones originales, en este caso 400 x 400 pixeles. La segunda versión de la imagen (derecha) una versión alternativa dimensionada a 200 x 200 pixeles. Así se verá …

Final

 

Por lo tanto tendremos esta pagina donde se muestran las imágenes (en mi ejemplo ImagenSinTemp.aspx) y también tendremos el archivo correspondiente al handler que muestra la miniatura (en mi ejemplo HandlerImagenes.ashx)

El diseño del archivo principal (el que contiene las imágenes) queda de la siguiente manera:

<%@ Page Language="vb" AutoEventWireup="false" CodeBehind="ImagenSinTemp.aspx.vb" Inherits="Sitio20.ImagenSinTemp" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >

<head runat="server">
    <title>Ejemplo miniaturistico</title>
</head>

<body>

    <form id="frmImagen" runat="server">

                <table width="620" border="1">
                <tr>
                    <td align="center">
                        Esta es la imagen original<br />
                        (400 x 400)</td>
                    <td align="center">
                        Replica mas peque&ntilde;a<br />
                        (200 x 200)</td>
                </tr>
                <tr>
                    <td align="center">
                        <asp:Image ID="imgOriginal" runat="server" ImageUrl="~/ejemplo.jpg" /></td>
                    <td align="center">
                        <asp:Image ID="imgMiniatura" runat="server" ImageUrl="HandlerImagenes.ashx" /></td>
                </tr>
            </table>

    </form>

</body>

</html>

Su code-behind simplemente en este caso tengo el Load de la pagina que setea la propiedad ImagenUrl de la imagen que muestra la versión miniatura. Nótese que se setea tanto la ruta completa del archivo como las dimensiones que deseo que tenga.


Partial Public Class ImagenSinTemp
    Inherits System.Web.UI.Page

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        imgMiniatura.ImageUrl = "HandlerImagenes.ashx?" & _
                                "archivo=" & HttpUtility.UrlEncode("D:\Desarrollo\NET\Sitio20\Ejemplo.jpg") & _
                                "&ancho=200" & _
                                "&alto=200"

    End Sub

End Class

Y a continuación el código correspondiente al handler de imagenes:


Imports System.Web
Imports System.Web.Services

Imports System.IO
Imports System.Drawing
Imports System.Drawing.Imaging

Public Class HandlerImagenes
    Implements System.Web.IHttpHandler

    Private Const __ANCHO_DEFECTO As Integer = 200
    Private Const __ALTO_DEFECTO As Integer = 200

    ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
        Get
            Return False
        End Get
    End Property

    Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest

        Dim sArchivo As String = String.Empty
        Dim iAncho As Integer = __ANCHO_DEFECTO
        Dim iAlto As Integer = __ALTO_DEFECTO

        context.Response.Clear()
        context.Response.ClearContent()

        If Not String.IsNullOrEmpty(context.Request("archivo")) Then
            If (File.Exists(context.Request("archivo"))) Then

                sArchivo = HttpUtility.UrlDecode(context.Request("archivo"))

                If Not String.IsNullOrEmpty(context.Request("ancho")) Then
                    iAncho = context.Request("ancho")
                Else
                    ResponderError(context, "No se ha especificado ancho", iAlto, iAncho)
                End If

                If Not String.IsNullOrEmpty(context.Request("alto")) Then
                    iAlto = context.Request("alto")
                Else
                    ResponderError(context, "No se ha especificado alto", iAlto, iAncho)
                End If

                ReponderImagen(context, sArchivo, iAncho, iAlto)

            Else
                ResponderError(context, "El archivo especificado no existe", iAlto, iAncho)
            End If
        Else
            ResponderError(context, "No se ha indicado archivo", iAlto, iAncho)
        End If

        context.Response.End()

    End Sub

    Public Shared Sub ReponderImagen(ByVal pContexto As HttpContext, _
                                     ByVal pArchivo As String, _
                                     ByVal pAncho As Integer, _
                                     ByVal pAlto As Integer)

        Dim bmpOut As System.Drawing.Bitmap = Nothing
        Dim tempStream As MemoryStream = New MemoryStream()

        Try
            Dim loBMP As New Bitmap(pArchivo)
            Dim loFormat As ImageFormat = loBMP.RawFormat

            Dim lnRatio As Decimal
            Dim lnNewWidth As Integer = 0
            Dim lnNewHeight As Integer = 0

            If loBMP.Width < pAncho AndAlso loBMP.Height < pAlto Then

                loBMP.Save(tempStream, bmpOut.RawFormat)

            Else
                If loBMP.Width > loBMP.Height Then

                    lnRatio = CDec(pAncho) / loBMP.Width
                    lnNewWidth = pAncho
                    Dim lnTemp As Decimal = loBMP.Height * lnRatio
                    lnNewHeight = CInt(Math.Truncate(lnTemp))
                Else

                    lnRatio = CDec(pAlto) / loBMP.Height
                    lnNewHeight = pAlto
                    Dim lnTemp As Decimal = loBMP.Width * lnRatio
                    lnNewWidth = CInt(Math.Truncate(lnTemp))
                End If

                bmpOut = New Bitmap(lnNewWidth, lnNewHeight)
                Dim g As Graphics = Graphics.FromImage(bmpOut)
                g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic
                g.FillRectangle(Brushes.White, 0, 0, lnNewWidth, lnNewHeight)
                g.DrawImage(loBMP, 0, 0, lnNewWidth, lnNewHeight)

                loBMP.Dispose()
                bmpOut.Save(tempStream, ImageFormat.Jpeg)

                pContexto.Response.ContentType = "image/jpg"
                pContexto.Response.BinaryWrite(tempStream.ToArray())

            End If

        Catch ex As Exception
            ResponderError(pContexto, "Error al crear imagen: " & ex.Message, pAlto, pAncho)
        End Try

    End Sub

    Public Shared Sub ResponderError(ByVal pContexto As HttpContext, ByVal pTexto As String, ByVal pAlto As Integer, ByVal pAncho As Integer)

        Dim tempStream As MemoryStream = New MemoryStream()
        Dim bitmap As New Bitmap(pAlto, pAncho)
        Dim g As Graphics = Graphics.FromImage(bitmap)

        pContexto.Response.ClearContent()
        pContexto.Response.ContentType = "image/jpg"

        g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None
        g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit
        g.Clear(Color.White)

        System.Windows.Forms.TextRenderer.DrawText(g, pTexto, _
                                                   New Font("Tahoma", 14, FontStyle.Regular, GraphicsUnit.Pixel), New Point(0, 0), _
                                                   Color.Black)

        bitmap.Save(tempStream, ImageFormat.Jpeg)

        pContexto.Response.BinaryWrite(tempStream.ToArray)

    End Sub

End Class

La propiedad IsReausable y el método ProcessRequest corresponden a la implementación de la interfaz IHttpHandler.

El bastante simple. En ProcessRequest se analizan los parámetros recibidos devolviendo la imagen con la cadena que indica el error donde corresponde y en caso de estar todo correcto el método ReponderImagen es quien se encarga de crear la imagen miniatura.

Queda a criterio de ustedes personalizar cómo se muestra el mensaje de error acorde a sus propias necesidades.

Saludos!

  1. Aún no hay comentarios.
  1. Aún no hay trackbacks

Deja un comentario

Fill in your details below or click an icon to log in:

Logo de WordPress.com

You are commenting using your WordPress.com account. Log Out / Cambiar )

Twitter picture

You are commenting using your Twitter account. Log Out / Cambiar )

Facebook photo

You are commenting using your Facebook account. Log Out / Cambiar )

Connecting to %s

Seguir

Get every new post delivered to your Inbox.