InicioArticulos y noticiasBases de datosProgramaciónForosInternetServiciosContratacionEmail
También puedes ver ...
SubVersion y Visual Studio
Usuarios del sistema en Windows (servicios)
Encriptación con AES Rijndael - Ejemplo
TripleDES - Un ejemplo practico en C#
Templates css para web
Insertar una marca de agua en documentos PDF
Serialización: XmlSerializer y BinaryFormater
Como modificar el web.config sin reiniciar la aplicacion ASP.NET
Conversiones de tipos personalizadas (VB y C#)
Compresión por gzip y deflate


Handler para manipular imagenes

Cada día es mas frecuente - imprescindible diria yo - que las aplicaciones Web permitan al usuario subir al servidor sus propias imagenes. El problema es que ese usuario no tiene porque saber los peligros de subir imagenes demasiado grandes y pesadas, o porque no, las imagenes deben ser grandes por requerimientos del sistema - como es mi caso -.

Estoy trabajando es un sistema que permite subir archivos de gran tamaño a la base de datos, fotos que deben visualizarse a través de una página web pero que deben permitir su descarga en alta definicion.

Una solucion fácil sería crear dos imagenes - una con menor calidad para mostrar en web y otra para descargar. Descarté esta solución ya que potencialmente el sistema va a trabajar con millones de imagenes, y duplicar toda esa información es un coste de almacenamiento excesivo.

La solución implementada ha sido con un manejador http, que recibe la petición a la imagen y la procesa para reducir su tamaño "al vuelo".

La idea es que los controles imagen no apunten directamente al archivo, sino que apunte al handler para manipular la imagen antes de servirla de vuelta. Por ejemplo, el código de mis webform es algó similar a esto. ImageUrl apunta a un handler http - BigImgHandler.ashx - pasando por query string la ruta de la imagen que queremos mostrar.

 

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

<asp:Image ID="Image1" runat="server"

ImageUrl="BigImgHandler.ashx?img=~/img/bigImage.jpg" />

</form>

El handler recibirá la ruta de la imagen real, la leerá del disco y la modifcará disminuyendo su tamaño antes de devolverla.

El esqueleto del handler se muestra a continuacion - únicamente implementamos los métodos que define la interfaz IHttpHandler, como puede observarse todo el trabajo se delega en el método ProcesarSolicitud que comentaremos más adelante.

 

using System;

using System.Data;

using System.Configuration;

using System.Linq;

using System.Web;

using System.Web.Security;

using System.Web.UI;

using System.Web.UI.HtmlControls;

using System.Web.UI.WebControls;

using System.Web.UI.WebControls.WebParts;

using System.Xml.Linq;

using System.Drawing.Imaging;

using System.Drawing;

using System.IO;

 

public class BigImgHandler: IHttpHandler

{

public int Width { get; set; }

public int Height { get; set; }

 

public virtual void Inicialize();

 

public bool IsReusable

{

get{

return true;}

}

 

public void ProcessRequest(HttpContext context)

{

ProcesarSolicitud(context);

}

 

// ... Implementacion de la clase

}

También definimos dos propiedades, Width y Height, que van a determinar el tamaño de salida final de la imagen, y un método Inicialize - pensado precisamente para asignar estas propiedades. El método es virtual lo que nos permitirá sobreescribirlo es clases derivadas - podrémos tener varios handler, que únicamente modifiquen el tamaño de salida.

Bien, vamos ahora con el método ProcesarSolicitud, que es quien realiza todo el trabajo.

 

private void ProcesarSolicitud(HttpContext context)

{

Inicialize();

context.Response.Clear();

string imgurl = context.Request["img"];

context.Response.ContentType = GetContentType(imgurl);

string path = VirtualPathUtility.ToAbsolute(imgurl);

path = context.Server.MapPath(path);

byte[] buffer = GetImagen(path, Width, Height);

context.Response.BinaryWrite(buffer);

}

El método recoge la url de la imagen que vamos a procesar, la convierte a una ruta local haciendo uso de la clase VirtulPathUtility y devuelve una salida binaria con una nueva imagen que genera el método GetImagen.

 
	byte[] GetImagen(String path, int width, int height)

{

using (FileStream fs =

new FileStream(path, FileMode.Open, FileAccess.Read))

{

using (Bitmap imgIn = new Bitmap(fs))

{

double y;

double x;

double ratio;

DeterminarRatio(width, height, imgIn,

out y, out x, out ratio);

using (System.IO.MemoryStream outStream =

new System.IO.MemoryStream())

{

using (Bitmap imgOut =

new Bitmap((int)(x * ratio), (int)(y * ratio)))

{

using (Graphics g = Graphics.FromImage(imgOut))

{

NewImage(path, imgIn, y, x, factor,

outStream, imgOut, g);

}

return outStream.ToArray();

}

}

}

}

}

 

private void NewImage(String path, Bitmap imgIn,

double y, double x, double ratio,

System.IO.MemoryStream outStream,

Bitmap imgOut, Graphics g)

{

g.Clear(Color.White);

g.DrawImage(imgIn,

new Rectangle(0, 0, (int)(ratio* x),

(int)(ratio * y)),

new Rectangle(0, 0, (int)x, (int)y),

GraphicsUnit.Pixel);

 

imgOut.Save(outStream, GetImageFormat(path));

}

Es aqui donde se realiza todo el trabajo ... se hacen varias cosas ...

  1. Lectura de la imagen original - en modo solo lectura (para que no haya errores al tratar archivo protegidor contra escritura)
  2. Calculo del ratio altura-anchura, para que la nueva imagen sea proporcional y no quede "deformada"
  3. Creación de la nueva imagen. Para ello se crea una nueva image (una nueva instancia de la clase Bitmap) con el ancho y alto calculado anteriormente, y se dibuja en ella el contenido de la imagen original.
  4. Finalmente se devuelve el nuevo archivo como un array de bytes.

Es importante destacar que:

  1.  La clase Bitmap no se refiere a un formato de imagen concreto. El formato lo proporcionamos al grabar la imagen.
  2.  La utilizacion de using garantiza que los recursos utilizados son liberados al finalizar la ejecución. 

El código se apoya en varias funciones auxiliares:

  • DeterminarRatio - que establece el ratio de altura-anchura de la imagen original para que la imagen resultante no se deforme
  • GetContentType - que devuelve el literal correspondiente al ContentType de cada tipo de imagen.
  • GetImageFormat - que retorna el tipo de la enumeracion ImageFormat necesario para crear la nueva imagen.

El código de esas funciones se muestra aqui.

 

private static void DeterminarRatio(int width, int height,

Bitmap imgIn, out double y, out double x, out double ratio)

{

y = imgIn.Height;

x = imgIn.Width;

ratio = 1;

 

if (width > 0)

{

ratio = width / x;

}

else if (height > 0)

{

ratio = height / y;

}

}

 

string GetContentType(String path)

{

switch (Path.GetExtension(path))

{

case ".bmp":

return "Image/bmp";

case ".gif":

return "Image/gif";

case ".jpg":

return "Image/jpeg";

case ".png":

return "Image/png";

default:

return "";

}

}

 

ImageFormat GetImageFormat(String path)

{

switch (Path.GetExtension(path))

{

case ".bmp":

return ImageFormat.Bmp;

case ".gif":

return ImageFormat.Gif;

case ".jpg":

return ImageFormat.Jpeg;

case ".png":

return ImageFormat.Png;

case ".ico":

return ImageFormat.Icon;

default:

return ImageFormat.Jpeg;

}

}

Por úlitmo comentar que esta solución es válida solo en determinadas circunstancias - ya que requiere de un uso extra de memoria y procesador. En mi caso las imagenes solo se muestran al hacer click en un enlace por lo que el uso de memoria y procesador compesa sobradamente el de almacenamiento.

El código completo de handler es el siguiente:

using System;

using System.Data;

using System.Configuration;

using System.Linq;

using System.Web;

using System.Web.Security;

using System.Web.UI;

using System.Web.UI.HtmlControls;

using System.Web.UI.WebControls;

using System.Web.UI.WebControls.WebParts;

using System.Xml.Linq;

using System.Drawing.Imaging;

using System.Drawing;

using System.IO;

 

public class BigImgHandler: IHttpHandler

{

public bool IsReusable

{

get{return true;}

}

 

public void ProcessRequest(HttpContext context)

{

ProcesarSolicitud(context);

}

 

public int Width { get; set; }

public int Height { get; set; }

 

public void Inicialize()

{

this.Width = 700;

}

 

 

private void ProcesarSolicitud(HttpContext context)

{

Inicialize();

context.Response.Clear();

string imgurl = context.Request["img"];

context.Response.ContentType = GetContentType(imgurl);

string path = VirtualPathUtility.ToAbsolute(imgurl);

path = context.Server.MapPath(path);

byte[] buffer = GetImagen(path, Width, Height);

context.Response.BinaryWrite(buffer);

}

byte[] GetImagen(String path, int width, int height)

{

using (FileStream fs =

new FileStream(path, FileMode.Open, FileAccess.Read))

{

using (Bitmap imgIn = new Bitmap(fs))

{

double y;

double x;

double ratio;

DeterminarRatio(width, height, imgIn,

out y, out x, out ratio);

using (System.IO.MemoryStream outStream =

new System.IO.MemoryStream())

{

using (Bitmap imgOut =

new Bitmap((int)(x * ratio), (int)(y * ratio)))

{

using (Graphics g = Graphics.FromImage(imgOut))

{

NewImage(path, imgIn, y, x, factor,

outStream, imgOut, g);

}

return outStream.ToArray();

}

}

}

}

}

 

private void NewImage(String path, Bitmap imgIn,

double y, double x, double ratio,

System.IO.MemoryStream outStream,

Bitmap imgOut, Graphics g)

{

g.Clear(Color.White);

g.DrawImage(imgIn,

new Rectangle(0, 0, (int)(ratio* x),

(int)(ratio * y)),

new Rectangle(0, 0, (int)x, (int)y),

GraphicsUnit.Pixel);

 

imgOut.Save(outStream, GetImageFormat(path));

}

 

private static void DeterminarRatio

(int width, int height, Bitmap imgIn,

out double y, out double x, out double ratio)

{

y = imgIn.Height;

x = imgIn.Width;

ratio = 1;

if (width > 0)

{

ratio = width / x;

}

else if (height > 0)

{

ratio = height / y;

}

}

 

string GetContentType(String path)

{

switch (Path.GetExtension(path))

{

case ".bmp":

return "Image/bmp";

case ".gif":

return "Image/gif";

case ".jpg":

return "Image/jpeg";

case ".png":

return "Image/png";

default:

return "";

}

}

 

ImageFormat GetImageFormat(String path)

{

switch (Path.GetExtension(path))

{

case ".bmp":

return ImageFormat.Bmp;

case ".gif":

return ImageFormat.Gif;

case ".jpg":

return ImageFormat.Jpeg;

case ".png":

return ImageFormat.Png;

case ".ico":

return ImageFormat.Icon;

default:

return ImageFormat.Jpeg;

}

}

}

Saludos,

DJK

 


 Versión para imprimir  Foros de consulta

 
Excelente por Mihai Mosor
Respuesta recibida el [27/05/2009 09:05:39]
El método handler veo que es más útil que el puntero que inicia la imagen directamente. Es parecido a aun array de punteros que se referencian a los objetos. Supongo que se está hablando de tamaño de imágenes de algunos MB, para tener una visión amplia sobre el tema.  

 
No se trata de punteros ... si... por Devjoker
Respuesta recibida el [27/05/2009 09:33:12]
No se trata de punteros ... sino de peticiones http. Son imagenes realmente grandes y es por eso que el coste de procesador y memoria compensa claramente frente al trafico de red y coste de almacenamiento. 

 
Gracias por aclararme la duda,... por Mihai Mosor
Respuesta recibida el [27/05/2009 03:47:29]
Gracias por aclararme la duda, es que me gustaría saber más sobre la manipulación de imágenes en las bases de datos. Seguiré leyendo los artículos del portal y estoy esperando tus tutoriales informativos. Un saludo.

 
cvc por cc
Respuesta recibida el [10/08/2009 12:43:41]
cc


Añadir comentario ... Para preguntar utiliza los foros
Autor:

Título:


Para preguntar utiliza los foros.



 Versión para imprimir

Handler para manipular imagenes
Autor: Pedro Herrarte Sánchez
Visitas: 2455 Fecha de publicación: 26/05/2009
Pedro Herrarte, es consultor independiente, ofreciendo servicios de consultoría, análisis, desarrollo y formación.

Posee mas de diez años de experiencia trabajando para las principales empresas de España.

Es especialista en tecnologías .NET, entornos Web, bases de datos (SQL Server y ORACLE) e integración de sistemas.

Es experto en desarrollo (C#, ASP.NET, VB.Net, T-SQL, PL/SQL, , ASP, CGI , C, Pro*C, Java, Essbase, Vignette, PowerBuilder y Visual Basic ...) y bases de datos (SQL Server y ORACLE).

Es fundador, diseñador y programador de www.devjoker.com.




Visitas: 111 | Comentarios: 3 | Archivo: Articulos
02/02/2010 iPad vs Telesketch
Visitas: 812 | Comentarios: 2 | Archivo: Articulos
Categorias: Humor
Visitas: 767 | Comentarios: 3 | Archivo: Articulos
Visitas: 3185 | Comentarios: 2 | Archivo: Articulos
Categorias: C#
Visitas: 14575 | Comentarios: 7 | Archivo: Articulos
Categorias: C#|Seguridad
Visitas: 1502 | Comentarios: 3 | Archivo: Articulos
Visitas: 697 | Comentarios: 2 | Archivo: Articulos
Visitas: 369 | Comentarios: 0 | Archivo: Articulos
Visitas: 2035 | Comentarios: 2 | Archivo: Articulos
Categorias: C#

Útimos temas recibidos en los foros ...
Solicitud certificado de afiliaciones a pensiones y cesantias proteccion por hz ... [Actualidad] 62 10/02/2010
certificado de pensiones del bbva horizonte por danger ... [Actualidad] 127 12/01/2010
certificacion cesantias porvenir por martha perez ... [Actualidad] 6 04/03/2010
cerificado fondo horizonte de pensiones y cesantias por carlos fideligno torres herrera ... [Actualidad] 148 18/01/2010
certificado de afiliacion por EDDY ... [Actualidad] 0 14/03/2010
Cursores por nopodes ... [ORACLE] 0 14/03/2010
peticion urgente mi nombre es juan pablo osorio cardona cc 71115218 por juancho ... [Actualidad] 0 14/03/2010
peticion por juancho ... [Actualidad] 0 14/03/2010
estrato de pencion por carlos antonio mateus gomez ... [Actualidad] 0 14/03/2010
quiero aprender c# por alcapego ... [C#] 4 16/02/2008
certificado pensiones horizonte por angela patricia rodriguez ... [Actualidad] 140 26/01/2010
certificado de penciones y cesantias por fredy ... [Actualidad] 2 29/01/2010

Access CGI JSP ORACLE UNIX
Actualidad HTML/DHTML/XHTML LINUX PHP Visual Basic .NET
ASP ISAPI MS DOS Power Builder Visual Basic 6.0
ASP.NET Java mySQL SQL WIN 98/NT/2000/XP
C# JavaScript Opinion SQL Server

devjoker  Te recomendamos además ...
27/08/2007 Enviar email con C#
31/03/2006 Operadores en PL/SQL    forma parte de...Tutorial PL/SQL
12/01/2006 Introducción a PLSQL    forma parte de...Tutorial PL/SQL
22/10/2005 Insertar datos. INSERT    forma parte de...Tutorial SQL
11/10/2006 Métodos    forma parte de...Tutorial C#
13/10/2006 Constructores    forma parte de...Tutorial C#
25/08/2007 Variables en Transact SQL    forma parte de...Tutorial de Transact SQL
22/10/2005 Vistas    forma parte de...Tutorial SQL
14/07/2007 Como encriptar texto y variables usando algoritmos estandard como DES o TRIPLEDES
21/11/2007 Desencriptar Texto y variables con .NET VB y C# (segunda parte)

 

Encuesta
¿A que perfil te adaptas mejor?




[Ver] [Votar]