In questo post vorrei proporvi un modo per creare immagini dinamiche (in maniera analoga a quanto già proposto in questo post) all’interno delle nostre pagine ASPX utilizzando il DataURI.
Partiamo dal definire cosa è il DataURI.
Il DataURI (o più esattamente lo schema data URI), la cui definizione esatta è rintracciabile seguendo il link, rappresenta un modo per includere all’interno delle nostre pagine web delle risorse (ad esempio grafiche, ma non solo).
Lo schema data URI compare già nelle specifiche dell’HTML 4.01.
I vantaggi di utilizzare lo schema data URI sono i seguenti:
- le risorse incorporate nella pagina non richiedono differenti chiamate al server come accadrebbe se le risorse fossero indicate con un normale URL. Si ha quindi un risparmi nel numero di richieste e si elimina il traffico dell’header delle stesse;
- per files di piccole dimensioni si ha un guadagno di tempo, poichè, generalmente, l’avvio della comunicazione TCP è lento;
- nelle comunicazioni HTTPS, il browser, di solito, richiede che tutti gli elementi della pagina siano scaricati in connessione sicura (pena la comunicazione all’utente che ci sono elementi non “sicuri”) e l’HTTPS aggiunge un ulteriore overhead alla trasmissione;
- i messaggi email possono, in questo modo contanere immagini senza che queste appaiano come degli allegati;
- se il browser ha un numero basso di connessioni simultanee, si ha un vantaggio di tempo.
Lo schema data URI, però, ha anche degli svantaggi:
- le risorse incorporate come data URI non sono cachate come le comuni risorse (ad esempio immagini) referenziate con un comune URL;
- alcuni browser ancora in circolazione (ad esempio IE7 con circa il 27% della distribuzione sul mercato a luglio 2010) non lo supportano;
- alcuni browser hanno una limitazione sulla lunghezza massima del data URI (ad esempio IE8 ha una limitazione a 32KB);
- l’encoding del data URI (in base 64) è mediamente 1/3 più largo dell’equivalente encoding binario;
- i software che si occupano di controllare la sicurezza dei siti lavorano male sui dati contanuti nello schema data URI.
Il formato di una risorsa espressa secondo lo schema data URI è il seguente:
data:[<MIME-type>][;charset=<encoding>][;base64],<data>
Se non si indica il base64, si intende che i dati sono espressi in ASCII.
Ad esempio, un’immagine in HTML è espressa nel seguente modo:
- <img src="data:image/png;base64,
- iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABGdBTUEAALGP
- C/xhBQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9YGARc5KB0XV+IA
- AAAddEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q72QlbgAAAF1J
- REFUGNO9zL0NglAAxPEfdLTs4BZM4DIO4C7OwQg2JoQ9LE1exdlYvBBeZ7jq
- ch9//q1uH4TLzw4d6+ErXMMcXuHWxId3KOETnnXXV6MJpcq2MLaI97CER3N0
- vr4MkhoXe0rZigAAAABJRU5ErkJggg==" alt="Red dot" />
Lo schema data URI può essere utilizzato anche all’interno di un CSS (per i browser che lo supportano) o per includere del JavaScript.
Capito cosa è lo schema data URI vediamo come possiamo utilizzarlo nelle nostre pagine ASPX.
Innanzitutto vediamo cosa si ottiene in termini di richieste e dati trasferiti quando includiamo delle immagini tramite data URI e in maniera classica.
Per fare questo realizziamo due pagine all’interno delle quali visualizzeremo le immagini contenute in una cartella nelle due modalità.
Il code behind della pagina che utilizza la tecnica standard è la seguente:
- Imports System.IO
- Public Class MultiImageStandard
- Inherits System.Web.UI.Page
- Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
- If Not IsPostBack Then
- CreateImages(10)
- End If
- End Sub
- Private Sub CreateImages(ByVal numImages As Integer)
- Dim imagePath = Server.MapPath("/images")
- Dim imageRep = New ImageRepository()
- Dim imageFiles = imageRep.GetAllImages(imagePath)
- For Each imageFile In imageFiles
- Dim image = New Image()
- Dim fi = New FileInfo(imageFile)
- image.ImageUrl = String.Format("/images/{0}", fi.Name)
- Me.Controls.Add(image)
- Next
- End Sub
- End Class
La classe ImageRepository enumera le immagini presenti nella cartella.
In sostanza creiamo un oggetto Image (web control) per ogni immagine ed impostiamo il sui ImageUrl. Il risultato ottenuto è il seguente:
Utilizzando IE9 e la developer toolbar (tasto F12) possiamo osservare il numero di chiamate eseguite verso il server per recuperare la pagina e le 12 immagini visualizzate.
Il code behind della pagina che utilizza lo schema data URI è il seguente:
- Public Class MultiImageDataUri
- Inherits System.Web.UI.Page
- Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
- If Not IsPostBack Then
- CreateImages(10)
- End If
- End Sub
- Private Sub CreateImages(ByVal numImages As Integer)
- Dim imagePath = Server.MapPath("/images")
- Dim imageRep = New ImageRepository()
- Dim imageFiles = imageRep.GetAllImages(imagePath)
- For Each imageFile In imageFiles
- Dim image = New Image()
- image.ImageUrl = CreateDataUri(imageFile)
- Me.Controls.Add(Image)
- Next
- End Sub
- Private Function CreateDataUri(ByVal imagePath As String) As String
- Dim image = New Drawing.Bitmap(imagePath)
- Dim base64String As String
- Using ms As New IO.MemoryStream
- image.Save(ms, image.RawFormat)
- base64String = Convert.ToBase64String(ms.ToArray())
- End Using
- Return String.Format("data:image/png;base64,{0}", base64String)
- End Function
- End Class
In questo caso, per ogni immagine da visualizzare, craiamo un oggetto Bitmap, ne ricaviamo la sua rappresentazione come array di Byte e, quindi, lo encodiamo in base64.
Il risultato, in questo caso, è il seguente:
Osserviamo una sola connessione per recuperare l’intera pagina.
Un altro utilizzo dello schema data URI può essere quello di visualizzare delle immagini dinamiche.
Supponiamo di voler fare in modo che data un’immagine venga applicata su di essa una scritta di watermark (esattamente come già avevamo fatto nel post).
Per realizzare tutto ciò è sufficiente creare un immagine bitmap a partire dall’immagine su cui applicare il watermark, recuperare il suo contesto grafico, disegnare il watermark su di essa e, infine encodarla in base64.
In sostanza possiamo scrivere:
- Imports System.Drawing
- Public Class DynamicImage
- Inherits System.Web.UI.Page
- Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
- If Not IsPostBack Then
- CreateImage("/images/BigImage/DominicanRepublic_DE-DE1561561743.jpg", "CodeTailor", Image1)
- End If
- End Sub
- Private Sub CreateImage(ByVal imagePath As String,
- ByVal watermark As String,
- ByVal imageControl As System.Web.UI.WebControls.Image)
- Dim imageFullPath = HttpContext.Current.Server.MapPath(imagePath)
- Dim image As New Bitmap(imageFullPath)
- Dim gr = Graphics.FromImage(image)
- Dim watermarkFont = New Font(FontFamily.GenericMonospace, _
- 10, FontStyle.Regular, GraphicsUnit.Pixel)
- Dim textSize = gr.MeasureString(watermark, watermarkFont)
- Dim x = 0
- While x < image.Width
- Dim y = 0
- While y < image.Height
- gr.DrawString(watermark, watermarkFont, Brushes.White, x, y)
- y = CInt(y + (3 / 2 * textSize.Height))
- End While
- x = CInt(x + (3 / 2 * textSize.Width))
- End While
- Dim base64String As String
- Using ms As New IO.MemoryStream
- image.Save(ms, image.RawFormat)
- base64String = Convert.ToBase64String(ms.ToArray())
- End Using
- imageControl.ImageUrl = String.Format("data:image/png;base64,{0}", base64String)
- End Sub
- End Class
Il risultato che otteniamo è il seguente:
Commenti