in

DotNetSide

Dot Net South Italy Developers User Group

Articoli

Articoli pubblicati dagli iscritti a .netSide

Un Generic Handler per gestire le immagini

Autore: Mario Ferrante
 
I Gestori HTTP costituiscono una delle caratteristiche fondamentali dell’architettura ASP.Net sin dalle sue prime versioni. Qualsiasi richiesta ad una pagina o ad una risorsa di un’applicazione ASP.net viene mappata e quindi servita da uno specifico Http Handler.Pagine .aspx, servizi web .asmx, user controls .ascx vengono tutti mappati attraverso gestori specifici inclusi nel framework quali Page Handler, Web Service Handler, ma è anche possibile costruire gestori personalizzati per aggiungere alla richiesta di una risorsa particolari funzionalità, come ad esempio elaborazioni delle immagini, creazione al volo di grafici, rendere inaccessibili alcuni file, creare estensioni personalizzate per le nostre pagine e così via.
In Asp.net i Gestori HTTP possono essere realizzati in due modi differerenti, Custom Handlers e Generic Handlers.:

  • I Custom Handlers sono blocchi di codice che entrano in azione ogni qualvolta viene chiamata una specifica risorsa come una pagina aspx, un immagine, o pagine con estensioni personalizzate. Questo tipo di gestore deve essere necessariamente registrato nel file di configurazione, sia esso machine.config o web.config, in base allo scope che noi vogliamo dare al gestore stesso, se a livello di macchina o di singola applicazione.
  • I Generic Handler invece sono file con estensione ashx che si comportano come una qualsiasi pagina aspx: devono essere richiamati direttamente, ad esempio tramite la Url di un Browser e sono soggetti alla compilazione “on-the-fly”. Inoltre i Generic Handler non necessitano di alcuna registrazione nel web.config

In questo articolo tratteremo proprio la costruzione di un Generic Handler ashx che ci permetterà di ridimensionare o tagliare al volo delle immagini attraverso dei parametri passati nella query string.
 
L’INTERFACCIA IHTTPHANDLER
Tutti i gestori http, compresi i Generic Handler sono classi che implementiamo l’interfaccia IHttpHandler, la quale espone un metodo ProcessRequest ed una proprietà di sola lettura IsReusable:

  • ProcessRequest accetta come parametro un oggetto HttpContext ed è il metodo nel quale viene implementata la logica di processo di una richiesta http.
  • IsReusable se True permette il riutilizzo della stessa istanza del gestore, altrimenti viene creata una nuova istanza ad ogni richiesta.

Di seguito l’interfaccia IHttpHandler che dobbiamo implementare:
 
interface IHttpHandler

   void ProcessRequest(HttpContext context); 

   bool IsReusable { get;}
}
 
 
FACCIAMO PRATICA: IMPLEMENTAZIONE DI UN CUSTOM HANDLER
 
In questo esempio, vediamo come implementare un semplice gestore che mi dia un messaggio a video ogni volta che viene richiesta una determinata pagina aspx, ad esempio default.aspx.
 
Apriamo Visual Web Developer e creiamo un nuovo sito Web, se non è già presente aggiungiamo la cartella App_Code alla nostra applicazione.In App_Code aggiungiamo una classe che chiameremo SimpleHandler.csLa prima cosa da fare è quindi quella di implementare nella nostra classe l’interfaccia IHttpHandler:
 
public class SimpleHandler:IHttpHandler
{
}
 
Ora non ci resta che implementare il metodo ProcessRequest e la proprietà IsReusable, l’obbiettivo di questo Handler è quella di scrivere a video il classico HELLO WORLD ogni volta che venga richiesta la pagina Default.aspx. avremo:
 
public class SimpleHandler : IHttpHandler

   public void ProcessRequest(HttpContext context) 
   { 
      context.Response.Write("<h2>Hello World</h2>"); 
   } 
   public bool IsReusable 
   { 
      get {return false; } 
   }
}
 
REGISTRAZIONE NEL WEB.CONFIG
Una volta creato il nostro gestore, il secondo step è quello di registrare il gestore stesso nel web.config :

<
httpHandlers> 

   <add path="Default.aspx" verb="*" type="SimpleHandler" />
</httpHandlers>

Possiamo compilare e quindi aprire la nostra pagina default.aspx, vedremo il nostro Hello World, senza aver implementato alcuna riga di codice nella pagina.

UN GENERIC HANDLER PER GESTIRE LE IMMAGINI

A questo punto costruiamo un Generic Handler per la gestione delle immagini, il cui obbiettivo è quello di
poter ridimensionare e tagliare un’immagine attraverso l’utilizzo di parametri passati al nostro file ashx in una query string.Aggiungiamo al nostro progetto un generic handler cliccando con il tasto destro sul nome del progetto e quindi su Add New Item e su Generic Handler, lo chiameremo ImageToolCs.ashx .
Come si può notare un file ashx, a parte la prima riga dichiarativa, non è altro che una classe che implementa IHttpHandler e quindi il metodo ProcessRequest e la proprietà IsReusable
 
<%@ WebHandler Language="C#" Class="ImageTool" %>
 
using System;
using System.Web;
 
public class ImageTool : IHttpHandler { 
   public void ProcessRequest (HttpContext context) {
   } 

   public
bool IsReusable { 
      get
         return false
      } 
   }

}
 
Attraverso una Querystring passeremo al gestore dei parametri che determineranno l’azione da compiere sull’immagine. I parametri sono:

  • imagepath: indica il percorso dell’immagine
  • width: quanto deve essere ridimensionata la larghezza dell’immagine, se il valore è 0 o width non è presente la larghezza verrà ridimensionata proporzionalmente all’altezza.
  • height: quanto deve essere ridimensionata l’altezza dell’immagine, se il valore è 0 o height non è presente l’altezza verrà ridimensionata proporzionalmente alla larghezza.
  • crop: se “1” indica al gestore che l’immagine deve essere tagliata.
  • sx: il punto di partenza sull’asse X per il taglio dell’immagine.
  • fx: il punto di arrivo sull’asse X per il taglio dell’immagine.
  • sy: il punto di partenza sull’asse Y per il taglio dell’immagine.
  • fy: il punto di arrivo sull’asse Y per il taglio dell’immagine.


Sx, Fx, Sy, Fy impostano i quattro punti del rettangolo che vogliamo ritagliare sull’immagine.
 
Aggiungiamo, quindi, al nostro handler l’implementazione del metodo ProcessRequest che valuta il percorso fisico dell’immagine passata attraverso la querystring imagepath e, in base al valore di crop, richiamerà il metodo Scale se l’immagine deve essere ridimensionata, ScaleAndCrop, invece, se l’immagine oltre ad essere ridimensionata deve anche essere tagliata.
 
public void ProcessRequest (HttpContext context) { 
   string img = context.Request.QueryString["imagepath"]; 
   if ((context.Request.QueryString["Crop"] != null) && (context.Request.QueryString["Crop"] != "0")) 
   { 
      ScaleAndCrop(context, img, Width, Height, Sx, Sy, Fx, Fy); 
   } 
   else
      Scale(context, img, Width, Height); 
   } 
   // fermo il resto della risposta 
   context.Response.End();
}
 
Il metodo Scale si occupa di ridimensionare l’immagine in base ai valori di width e height e salva quindi l’immagine elaborata in uno stream di output:
 
public void Scale(HttpContext context, string pathImage, int width, int height)

   // recuperiamo l’estensione dell’immagine 
   string contentType = pathImage.Substring(pathImage.LastIndexOf(".")); 
 
   // creiamo un oggetto Bitmap che rappresenta la nostra immagine 
   using (Bitmap bmp = (Bitmap)(System.Drawing.Image.FromFile(context.Server.MapPath(pathImage)))) 
   { 
 
      // controlliamo se width e height sono maggiori di 0 
      if (width > 0 || height > 0) 
      { 
         //se solo height=0, l'altezza viene ridotta proporzionalmente alla larghezza 
         if (height == 0) 
            
height = Convert.ToInt32(((float)(width) / (float)(bmp.Width)) * bmp.Height); 
 
         //se solo height=0, l'altezza viene ridotta proporzionalmente alla larghezza 
         if (width == 0) 
            width = Convert.ToInt32(((float)(height) / (float)(bmp.Height)) * bmp.Width); 

                  //Ora viene creata la thumbnail e salvata in un output di stream 
                  
using (Bitmap thumb = new Bitmap(bmp, width, height)) 
         { 
            using (Graphics myGraphic = Graphics.FromImage(thumb)) 
            { 
               myGraphic.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; 
               myGraphic.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; 
               myGraphic.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; 
               Rectangle myrectangle = new Rectangle(0, 0, width, height); 
               myGraphic.DrawImage(bmp, myrectangle); 
               //salvo in uno stream di output 
               if (contentType.ToUpper().EndsWith(".JPEG") || contentType.ToUpper().EndsWith(".JPG")) 
               { 
                  thumb.Save(context.Response.OutputStream, ImageFormat.Jpeg); 
               } 
               else 
               { 
                  thumb.Save(context.Response.OutputStream, ImageFormat.Gif); 
               } 
            } 
         } 
      } 
      else 
      { 
         // Se width e height sono entrambi uguali a 0, l’immagine viene salvata nello stream cosi com’è 
         if (contentType.ToUpper().EndsWith(".JPEG") || contentType.ToUpper().EndsWith(".JPG")) 
         { 
            bmp.Save(context.Response.OutputStream, ImageFormat.Jpeg); 
         } 
         else 
         { 
            bmp.Save(context.Response.OutputStream, ImageFormat.Gif); 
         } 
      } 
   }


 

Infine realizziamo il metodo ScaleAndCrop il quale non solo si occupa di ridurre l’immagine, ma anche di tagliarla in base ai parametri passati nella query string. L’implementazione di questo metodo è simile a quella del precedente, analizziamo la parte in cui l’immagine viene tagliata:


using (Bitmap thumb = new Bitmap(width, height, bmp.PixelFormat))

{
   
using (Graphics mygraphic = Graphics.FromImage(thumb))
   
{
      
mygraphic.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
      
mygraphic.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
      
mygraphic.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
      
Rectangle myrect = new Rectangle(0, 0, width, height);
      
mygraphic.DrawImage(bmp, myrect);

 
      
// A questo punto viene costruito il Rettangolo di ritaglio
      
Rectangle recCrop = new Rectangle(sx, sy, (fx - sx), (fy - sy));
      
using (Bitmap bmpCroc = new Bitmap(recCrop.Width, recCrop.Height, bmp.PixelFormat))
      
using (Graphics mygr = Graphics.FromImage(bmpCroc))
      
{
         
Rectangle destRect = new Rectangle(0, 0, (fx - sx), (fy - sy));
         
mygr.DrawImage(thumb, destRect, recCrop.X, recCrop.Y, recCrop.Width, recCrop.Height, GraphicsUnit.Pixel);

         if (contentType.ToUpper().EndsWith(".JPEG") || contentType.ToUpper().EndsWith(".JPG"))
         
{
            
bmpCroc.Save(context.Response.OutputStream, ImageFormat.Jpeg);
         
}
         
else
         
{
            
bmpCroc.Save(context.Response.OutputStream, ImageFormat.Gif);
         
}
      
}
   }
}

E’ possibile analizzare il codice completo, anche in Visual Basic, nell’allegato all’articolo.

COME UTILIZZARE IL NOSTRO GENERIC HANDLER

A questo punto non ci resta che provare il nostro generic handler.
Digitiamo nella barra degli indirizzi di Internet Explorer o di un altro Browser:

http://localhost/generichandler/imagetool.ashx?imagepath=images/tramonto.jpg&width=300

In questo modo non facciamo altro che dire al nostro Handler di ridurre la larghezza dell’immagine Tramonto.jpg a 300px e di ridurre l’altezza in maniera proporzionale alla larghezza.
Se noi invece digitiamo:

 

http://localhost/generichandler/imagetool.ashx?imagepath=images/tramonto.jpg&width=300&crop=1&sx=50&fx=200&sy=50&fy=225

Il risultato che otteniamo è mostrato nella seguente immagine:

 

L’immagine non è solo stata ridotta ma anche tagliata in un rettangolo di 150px di larghezza e 175px di altezza.


E’ anche possibile chiamare il nostro
gestore attraverso l’attributo ImageUrl di un controllo Image, oppure attraverso l’attributo src di img:


<asp:Image Id=”img”  runat=”server” ImageUrl=”imagetool.ashx?imagepath=……&width=…&crop=1&…/>

 

<img src=”imagetool.ashx?imagepath=……&width=…&crop=1&sx=…&fx=…&sy=…&fy=…” />

In questo modo è possibile utilizzare il gestore all’interno di pagine aspx o anche di semplici pagine html.

 

CONCLUSIONI
Un Generic Handler è senza dubbio l’approccio più semplice per affrontare gli  HTTP Handlers, il fatto che esso non debba essere registrato nel web.config e  che non debba essere compilato in un assembly rende il suo utilizzo molto più immediato rispetto ad un Custom Handler.
Però gli stessi vantaggi di utilizzo di questo tipo di Handler possono diventare svantaggi ogni qual volta si ha bisogno di creare gestori che funzionano a livello di macchina e non di singola  applicazione, oppure ho la necessita di creare gestori che controllino pagine con estensioni personalizzate e che quindi richiedono di essere mappate su IIS. In tutti questi casi l’utilizzo dei Custom Handlers diventa inevitabile.


 

Only published comments... May 29 2006, 12:09 AM by Fabio.Cozzolino
Filed under:

Comments

 

Mario Ferrante's Blog said:

Qui &#232; possibile leggere il mio primo articolo sui Generic Handlers in Asp.net 2.0.Questo tipo di gestore...
May 30, 2006 9:18 AM
 

Mario Ferrante's Blog said:

Qui &#232; possibile leggere il mio primo articolo sui Generic Handlers in Asp.net 2.0.Questo tipo di gestore...
May 30, 2006 9:19 AM
 

Blog di Vito Arconzo said:

May 30, 2006 5:12 PM
 

Mighell's blog said:

Oggi girovagavo un po’ senza meta tra i settaggi di Community Server (su cui gira .netSide) e sono andato...
June 4, 2006 1:05 PM
 

Mighell's Blog said:

June 4, 2006 1:06 PM
 

Mighell's blog said:


Oggi girovagavo un po’ senza meta tra i settaggi di Community Server (su cui gira .netSide) e sono...
June 4, 2006 1:09 PM

About Fabio.Cozzolino

Fabio Cozzolino inizia la sua carriera da sviluppatore quando riscopre quello strano libricino blu con la scritta "BASIC" dato a corredo del suo Commodore 64. Da allora un susseguirsi di linguaggi, tra cui COBOL e Visual Basic, lo ha accompagnato fino alla conoscenza di .NET Framework. Dopo un periodo passato a dedicarsi allo sviluppo di applicazioni Windows, si è poi concentrato e appassionato al Web, approdando infine alla progettazione e realizzazione di applicazioni distribuite. E' MCAD.net e scrive regolarmente per la rivista italiana ioProgrammo. E' co-fondatore e Presidente di .netSide e ha scritto il libro Windows Communication Foundation uscito nel mese di novembre 2007 in allegato alla rivista ioProgrammo.

Powered by Community Server (Commercial Edition), by Telligent Systems