jblog: news e appunti per webmaster
 

MHTML Shake

Il Mime HTML e lo schema data:URL sono due importanti ingredienti nella ricetta che ti permetterà di ridurre le richieste HTTP.

  • Una pagina Mime HTML è un tipo di pagina web in cui è possibile scriverci sia il codice html sia dei dati come immagini, oggetti flash, file audio
  • Lo schema data:URL è una tecnica grazie alla quale è possibile inserire i dati nella pagina web (che abbiamo già visto nell’articolo Ridurre le richieste HTTP (parte 2): come unire e combinare immagini, script, css e che riprenderò per evitarti di fare su e giù per il blog)

Non utilizzeremo queste due tecniche separatamente ma ci serviranno per creare il mix esplosivo :-) per caricare quanti più dati possibile in una sola richiesta http: quella della pagina web.

Lo schema data:URL

Per capire bene il Mime HTML è necessario sapere come codificare i file in base64 e come utilizzarli all’interno della pagina per poi vedere l’intera struttura di un documento MHTML.

Il vantaggio fondamentale di questa tecnica consiste nel risparmio di tante richieste http quanti sono gli oggetti e le immagini codificati in base64 che inseriamo nella pagina.

Vediamo subito un esempio.

<IMG ALT="Foto" SRC="data:image/gif; base64,
	R0lGODdhMAAwAPAAAAAAAP///ywAAAAAMAAwhhx4dbgYKAAA7B72VFgl2Tf7452
	AAAC8IyPqcvt3wCcDkiLc7C0qwyGHhSWpjQu5yqmCYsapyuvUUlvONmOZtfzgFz
	ByTB10QgxOR0TqBQejhRNzOfkVJ+5YiUqrXF5Y5lKh/DeuNcP5yLWGsEbtLiOSp
	a/TPg7JpJHxyendzWTBfX0cxOnKPjgBzi4diinWGdkF8kjdfnycQZXZeYGejmJl
	ZeGl9i2icVqaNVailT6F5iJ90m6mvuTS4OK05M0vDk0Q4XUtwvKOzrcd3iq9uis
	F81M1OIcR7lEewwcLp7tuNNkM3uNna3F2JQFo97Vriy/Xl4/f1cf5VWzXyym7PH" />

Dopo la paura iniziale, in genere ci si domanda: cosa sono quei caratteri e come faccio a inserirli?

Quell’ammasso di caratteri è la codifica in base64 dell’immagine. In pratica, è il contenuto del file immagine codificato in base64.

Per codificare un’immagine in base64 si utilizza una semplice funzione php: base64_encode.

L’ultima questione riguarda il tipo MIME che si può ottenere in due modi:

  • utilizzando la funzione mime_content_type, dopo aver abilitato l’estensione php_mime_magic.dll.
  • leggendo le informazioni del file grazie le funzioni File Information di php. Era un’estensione PECL, ma ora non lo è più. Bisogna utilizzare la libreria esterna magic_open

Nel caso non si possano utilizzare queste funzioni si dovrà scrivere manualmente, il che presuppone che si conosca il tipo dell’oggetto o dell’immagine da inserire.

Come inserire un’immagine in base64 (senza funzioni per il tipo MIME)

<img src="data:image/jpeg;base64,<?
echo base64_encode(file_get_contents("img.jpg")); ?>" alt="Immagine" />

Come inserire un’immagine in base64 (con funzioni per il tipo MIME)

<? $mime = mime_content_type("img.jpg"); ?>
<img src="data:<? echo $mime; ?>;base64,<?
echo base64_encode(file_get_contents("img.jpg")); ?>" alt="Immagine" />

Script, demo e download

Per vedere lo script vai a http://test.jblog.it/img_base64/index.txt

Per vedere una demo vai a http://test.jblog.it/img_base64/

Per scaricare l’esempio vai a http://download.jblog.it/img_base64.zip

Mime HTML

Il termine “Mime HTML” sta per “Multipurpose Internet Mail Extensions HyperText Markup Language“.

Per avere un’idea di quella che può essere una pagina mhtml (di estensione .mht) pensiamo ad un’email con dei file allegati (che possono essere anche delle immagini presenti all’interno del testo). I file che vengono inviati tramite email, sottoforma di allegati, vengono codificati in base64 e successivamente inseriti nell’intestazione dell’email. Quando il nostro client riceve l’email, prima decodifica gli allegati per renderli fruibili ed eventualmente pronti per il download. Una pagina MHTML funziona grosso modo così.

ATTENZIONE. Una pagina MHTML non deve necessariamente essere quella in cui viene scritto il codice html. Infatti può esserlo anche un file css che utilizza delle immagini (che vedremo successivamente).

Struttura di una pagina MHTML

Schema Pagina MHTMLGeneralmente una pagina di questo tipo è composta da 3 mattoni fondamentali:

  • nell’intestazione si specifica che il file è di tipo “multipart/related”, il separatore dei dati codificati in base64 e il tipo di contenuto del file (es. “text/html”)
  • la seconda parte è quella relativa ai dati codificati in base 64 separati dal separatore specificato nell’intestazione e definiti dai rispettivi campi “Content-Location”, “Content-Type” e “Content-Transfer-Encoding” (naturalmente, quest’ultimo viene impostato con “base64″). Possono trovarsi anche in fondo alla pagina, il che è meglio poiché si da priorità al caricamento del codice
  • nella terza parte viene scritto il corpo della pagina (o del messaggio, nel caso delle email) in html, css o altro, all’interno del quale verranno richiamati i dati specificati nella seconda parte del documento

Esempio di documento

L’esempio seguente è una pagina web con allegate 2 immagini, e segue lo schema in figura.

Purtroppo Internet Explorer gestisce diversamente i dati allegati e ci costringe a scrivere due versioni di codice. Tuttavia, questa è una delle rare volte in cui preferisco IE poiché con il browser Microsoft i dati in base64 vengono scritti una sola volta e possono essere richiamati infinite volte all’interno del documento grazie alla stringa specificata in “Content-Location”. Con gli altri browser (Firefox, Opera, Safari, Chrome) è necessario scrivere i dati codificati per ogni utilizzo dell’oggetto o immagine, il che rende il tutto più pesante e ci costringe a pensare due volte prima di decidere di inviare un’oggetto all’interno del documento MIME Html.

<!--
MIME-Version: 1.0
Content-Type: multipart/related; boundary="|b|o|u|n|d|a|r|y|"

--|b|o|u|n|d|a|r|y|
Content-Location:2bmp24869
Content-Type:image/bmp
Content-Transfer-Encoding:base64

Qk0eCgAAAAAAADYEAAAoA[...caratteri...]QEBAQEBAQEBZA==
--|b|o|u|n|d|a|r|y|
Content-Location:1bmp21242
Content-Type:image/bmp
Content-Transfer-Encoding:base64

Qk12CwAAAAAAAD[...caratteri...]JCQkJCQkJ
-->
<!--
--|b|o|u|n|d|a|r|y|
Content-Location:5bmp92853
Content-Type:image/bmp
Content-Transfer-Encoding:base64

DCwmoA2NiOAKioQ[...caratteri...]QDz87gA0dGCALq6X
-->
<!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"  dir="ltr" lang="en-US">
<head>
  <title>Titolo pagina</title>
</head>
<body>

	<object data="data:image/bmp;base64,Qk0eCgAAAAAAADYEAAAoA[...caratteri...]QEBAQEBAQEBZA==">
		<img src="mhtml:http://localhost/jblog_script/mhtml_dataurl/index2.php!2bmp24869" alt="img3" />
	</object>

	<div style=" width:200px; height:200px; background-image:url(data:image/bmp;base64,Qk12CwAAAAAAAD[...caratteri...]JCQkJCQkJ); *background-image:url(mhtml:http://localhost/jblog_script/mhtml_dataurl/index2.php!1bmp21242);"></div>

</body>
</html>

Il fatto che queste immagini abbiano questo “strano” formato non ci deve assolutamente far preoccupare. Gli utilizzi sono quelli di sempre: all’interno del tag <img>, come sfondo di un elemento grazie alle regole css, ecc.

Compatibilità con IE. Per gestire la compatibilità con il browser Internet Explorer è necessario inserire l’immagine compatibile con IE all’interno dei tag <object> in cui viene specificata l’immagine in un formato compatibile con i restanti browser. I risultati sono i seguenti:

  • se si accede alla pagina con un browser diverso da IE, il tag object funzionerà correttamente mostrando l’immagine; il tag <img> al suo interno non verrà preso in considerazione dal browser
  • nel caso in cui si acceda con Internet Explorer, poiché il tag <object> non funzionerà verrà considerato il tag <img> in cui l’immagine specificata è in un formato compatibile con IE

Regole da rispettare. Per concludere vorrei evidenziare alcune importanti caratteristiche riguardo il formato di un documento MHTML:

  • “Mime Version” e “Content-Type” devono trovarsi obbligatoriamente in testa alla pagina, prima del <!Doctype…>
  • Il primo separatore può essere separato a piacere dall’intestazione del documento
  • Il separatore (boundary) deve trovarsi esattamente fra i dati in base64 dell’oggetto precedente e le intestazioni dell’oggetto successivo, senza ulteriori “a capo”, “spazi”, “tab”, e altri caratteri
  • I vari oggetti possono essere separati solo da “fine” (–>) e “inizio” (<!–) commenti, stando attenti a non inserire altri caratteri tra <!– e il separatore; invece, fra il contenuto in base64 e –> possono esserci caratteri di escape (a capo, tab, ecc.)
  • Prima del separatore è necessario inserire due trattini “–”
  • Riguardo IE, l’indirizzo da scrivere dopo mhtml: deve essere identico all’indirizzo visibile nella barra degli indirizzi. Questo indirizzo si ottiene dalla variabile riservata $_SERVER['REQUEST_URI']

Utilizzare concretamente Mime HTML e data:URL

Ecco come immagino la generazione di un documento Mime HTML con lo schema data:URL.

Schema MHTML e data:URL, processoPrima fase. Gli oggetti e le immagini vengono codificati in base64 e sistemati all’interno del documento.

Seconda fase. Il browser legge il codice html e decodifica i dati allegati portandoli al loro formato primitivo: le immagini ritornano immagini, gli oggetti flash ritornano animazioni, ecc.

La classe MimeHtmlHandler

Per automatizzare il tutto ho sviluppato una piccola classe PHP che con un paio di righe ti permetterà di inserire all’interno del documento MHtml un’immagine precedentemente allegata.

N.B. la proprietà *background-image serve per rimpiazzare la prima background-image nel caso questa non funzionasse. In particolare, la proprietà *background-image viene utilizzata per IE mentre background-image funziona con i restanti browser.

Il codice dell’esempio precedente è stato ottenuto da questo script PHP:

<?
// ( 1 ) Crea l'oggetto
$mhh = new MimeHtmlHandler("http://test.jblog.it","|b|o|u|n|d|a|r|y|","MIME Version 1.0");

// ( 2 ) Header "Content-Type" e "MIME Version"
$mph->printHeader();

?>
<!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"  dir="ltr" lang="en-US">
<head>
  <title>mhtml test page</title>
</head>
<body>
  <div id="test1">test #1</div>
  <div id="test2">test #2</div>

  <object data="<? $mhh->inc("2.bmp","image/bmp"); ?>">
	  <img src="<? $mhh->incIE("2.bmp"); ?>" alt="img3" />
  </object>

 <div style=" width:200px; height:200px; background-image:url(<? $mhh->inc("1.bmp","image/bmp"); ?>); *background-image:url(<? $mhh->incIE("1.bmp"); ?>);"></div>

</body>
</html>
<?
// ( 3 ) Crea la lista dei dati (per IE)
$mhh->incAll();
?>

Caratteristiche principali della classe MimeHtmlHandler

La classe ha due array privati: il primo è quello in cui vengono conservati gli oggetti che vengono inseriti nella pagina in modo da evitare di codificarli e impostarli nuovamente per un successivo inserimento; il secondo è quello relativo ai nomi in codice specificati in “Content-Location” quando si stampa la lista dei dati codificati che verrà utilizzata solo da Internet Explorer.

Le funzioni da utilizzare sono solo due: inc e incIE. Queste due funzioni permettono di inserire oggetti in formati compatibili con tutti i browser e con IE. Inoltre, in base al tipo e al numero di argomenti passati è possibile creare da zero un oggetto o richiamarne uno già inserito.

Quando si vuole stampare un’oggetto già inserito precedentemente il nome del file verrà cercato nell’array degli oggetti. La ricerca viene effettuata a partire dall’ultimo elemento in quanto sono molto più probabili gli inserimenti consecutivi di immagini rispetto a quelli richiesti in punti molto diversi e lontani di una pagina.

Le 3 istruzioni fondamentali

  1. Crea l’oggetto della classe specificando l’url di base delle immagini, il separatore (boundary) e la versione MIME
  2. Stampa l’intestazione “Content-Type” e “MIME Version”
  3. In fondo alla pagina viene stampata la lista dei dati in base64 che servono a Internet Explorer, mentre per gli altri browser i dati codificati vengono specificati all’interno del tag in cui si utilizzano

Come inserire un’immagine

Sono state create due funzioni distinte per l’inserimento di un’immagine compatibile con IE o con gli altri browser (rispettivamente incIE e inc). Analizziamo i casi più comuni relativi all’utilizzo di queste funzioni.

  • Inserire un’immagine in modo che sia compatibile con tutti i browser. Richiamare la funzione inc() all’interno dell’attributo “data” del tag <object>, passando come parametri il percorso del file e il tipo mime. All’interno dei tag <object> e </object> inserire il tag <img> richiamando la funzione incIE() all’interno dell’attributo “src”. Alla funzione incIE basterà passare solo il nome del file grazie al quale verranno recuperati gli altri dati relativi all’oggetto da stampare.
  • Inserire un’immagine esistente. Alle funzioni inc() e incIE() basterà passare il nome del file.

Demo e download

Per vedere una demo vai su http://test.jblog.it/mhtml1/

Per scaricare la classe MimeHtmlHandler e l’esempio vai su http://download.jblog.it/mhtml1.zip

Esempio2. CSS e data:URL

Per utilizzare la classe MimeHtmlHandler all’interno di un file CSS, la prima operazione da fare è impostare il server Apache in modo che processi i file CSS come file PHP. Per fare ciò basterà aggiungere queste righe:

<FilesMatch "myfile.css$">
	AddHandler application/x-httpd-php .css
</FilesMatch>

Infine componiamo il file css:

  • processandolo come file php si perde la natura del file css che ripristiniamo inviando l’header “Content-Type:text/css”
  • includiamo la classe MimeHtmlHandler, e richiamiamo le prime due funzioni fondamentali (costruttore e printHeader() )
  • componiamo il corpo del file css utilizzando, dove occorrono, le funzioni inc() e incIE()
  • infine, richiamiamo l’ultima funzione fondamentale incAll() per stampare la lista dei file che servirà ad Internet Explorer
<? header("Content-Type: text/css"); ?>
/*<?
include("MimeHtmlHandler.php");

// ( 1 ) Crea l'oggetto
$mhh = new MimeHtmlHandler("http://localhost","|b|o|u|n|d|a|r|y|","");

// ( 2 ) Header "Content-Type" e "MIME Version"
$mhh->printHeader();
?>*/

#test1 {
  background-image: url("<? $mhh->inc("2.bmp","image/bmp"); ?>"); /* normal */
  *background-image: url(<? $mhh->incIE("2.bmp"); ?>); /* IE < 8 */
}

#test2 {
  background-image: url("<? $mhh->inc("1.bmp","image/bmp"); ?>"); /* normal */
  *background-image: url(<? $mhh->incIE("1.bmp"); ?>); /* IE < 8 */
}

div {
  width: 100px;
  height: 100px;
  font: bold 24px Arial;
}
/*<?
// ( 3 ) Crea la lista dei dati (per IE)
$mhh->incAll();

?>*/

Demo e download

Per vedere una demo vai su http://test.jblog.it/mhtml2/

Per scaricare la classe MimeHtmlHandler e l’esempio vai su http://download.jblog.it/mhtml2.zip

Considerazioni finali

Nei precedenti esempi abbiamo visto solo come utilizzare queste tecniche per allegare le immagini, anche perché è questa la situazione più frequente. Tuttavia, nello stesso modo potresti allegare facilmente oggetti flash e applet Java: nello stesso modo!

Compatibilità IE. Ho testato queste tecniche con Internet Explorer, dalla versione 5.5 alla 8 e non ho riscontrato grossi problemi. Una piccola anomalia si verifica quando si aggiorna la pagina più volte e le immagini non vengono caricate. Sinceramente non so da cosa può dipendere.

Queste tecniche, in confronto ai CSS Sprite sembrano “roba da smanettoni” a causa dell’ammasso di numeri e lettere che genera la codifica in base64 e dell’utilizzo (leggero) di php. Non farti ingannare… In fondo cosa c’è di così difficile?!? Dovrai solo pensare alcuni minuti alle immagini da allegare ai documenti (html, css, ecc.) e analizzare un po’ le velocità di caricamento per capire fino a che punto potranno avvantagiarti. Io dico di provarci! ;-)

Non escludo che nella classe MimeHtmlHandler ci possano essere dei bug. Per qualsiasi problema, chiarimento e curiosità sarei felice se mi contattattassi. Dimmi com’è andata e se queste tecniche ti sono state utili: non vedo l’ora di sapere gli esiti dei tuoi esperimenti!

Crediti

Phpied.comdata:urls – what are they and how to use them

Phpied.comMHTML – when you need data: URIs in IE7 and under

Foto1. Vientiane 04 – Pink Dragonfruit shake di Ben Beiske

Articoli correlati
Articoli casuali (stesse categorie)
Commenti
Trackbacks
.1
3 Giugno 2009 alle 23:50

Ottimo articolo Giacomo! Chiaro anche ai meno esperti e sicuramente utile. Spenderò un po’ di tempo a fare delle prove!

.2
4 Giugno 2009 alle 12:17

@Mauro Accornero: bisogna fare delle prove, ma i risultati saranno sicuramente ottimi.

Grazie alla codifica in base64 è possibile inviare file da uno script lato server con Ajax…

.3
4 Giugno 2009 alle 23:55

Con il base64 si possono fare molte cose carine e penso abbia un buon potenziale l’unica pecca di cui sono a conoscenza per il momento è il limitato supporto di internet explorer (anche la versione 8 mi sembra riservi qualche limite) ma se dovessi utilizzare solo le tecnologie supportate da ie cambierei lavoro :)

.4
5 Giugno 2009 alle 01:24

Con Explorer ha un comportamento strano.
Se si va sulla pagina probabilmente non funziona, ma se si clicca sulla barra degli indirizzi e si preme invio (una specie di aggiornamento manuale) funziona!
Ci ho sbattuto la testa un po’, poi mi sono arreso. Per adesso…

Puoi usare questi tag:   <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

ATTENZIONE. Se inserisci più di 2 link il commento verrà messo in coda, quindi non sarà visibile subito.

jblog: news e appunti per webmaster