Datová URL pomohou s malými soubory

Ve světě nových webových technologií se nashromáždilo zase několik drobností a novinek, o nichž jsme na Zdrojáku nepsali. Teď je vhodná příležitost to trochu napravit a na některé se podívat. Začneme datovými URL, které sice nejsou až taková novinka, ale někteří webdesignéři a kodéři o nich stále nevědí.
Seriál: Webdesignérův průvodce po exotických krajích (3 díly)
- Datová URL pomohou s malými soubory 8. 8. 2011
- Protokol HTTP 12. 9. 2011
- Novinky protokolu WebSocket a režim fullscreen 7. 11. 2011
Nálepky:
Datová URL někteří vývojáři vůbec neznají (a diví se „bordelu v HTML“), jiní je považují za módní výstřelek, a další je bez většího povyku prostě používají. Oč vlastně jde a jak to vypadá?
Co jsou DataURL?
DataURL (někdy uváděno jako DataURI, ovšem RFC2397 hovoří o DataURL) je URL se schématem „data:“ a jeho obsahem jsou binární data. Příklad vše ozřejmí:
Představme si, že máme stránku, a na ní máme miniikonku, řekněme, pro tisk souborů. Takto: Ikonka je ve formátu png, má rozměry 16×16px a zabírá 629 bajtů.
Všichni známe běžný způsob vložení do stránky: <img src="https://www.zdrojak.cz/wp-content/uploads/ukazka/novinky/print.png" alt="print">
. Funguje, je ověřený, tak co je na tom za vědu?
Každý takový objekt je totiž přenášen zvlášť. Otevře se pro něj spojení, server je požádán o data pro tento soubor, on je vrátí a prohlížeč to celé zobrazí. Kvůli 629 bajtům letěly po síti kilobajty dotazů, odpovědí, servisních informací… Možná požadavek čekal ve frontě. Pokud je takových obrázků na stránce víc, pak data létají a fronty čekají… Schválně si zkuste změřit objem dat, která jsou přenesena při takovém dotazu, třeba na úrovni TCP – stovky bajtů hlaviček, cookies a dalších „neužitečných“ dat. Efektivita takových dotazů je opravdu velmi nízká.
Pro tyto situace je někdy výhodnější použít DataURL, kde jsou samotná binární data přímo součástí URL. Výše uvedená ikonka bude do kódu vložena jako
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8 /9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZT wAAAIHSURBVHjajFNBaxNBFP6SLNq0WGNSsS0ohFiiFUx6UNFCLsmph4jgqdKL0KgI/QMe7MV DEAQF8VDoMVtKoejB0OToRTQRDxpcmrC2SXSppG7ajV2yScadWTam6Sb4wdth3vve997M7LMR QmBi40uVZL5J+CD+Rjeuek/h0W2/rdtv6xS4+fQ9Wb4/BYfdhmbrn/+XUsfqJxnFbQlLD64cE rF3K9Lk/K4douxgltsBfKedmLk8hrPnRvFkTSB9BaxQ3lXhdTVx78bIkeNxVgkDnKGrNlpwHn NA2FH1XRPXvM4jXMbMZDIkHo+TXkKDzgFmJmKxGEmn04xvF0WRZLNZhMNh9OqGdtGJ2TtzSCa TrDCXSqUQCoXgdrsR2H+JZ/G3Pe8irVtAt4sXHurfW0gkEuBkWcb5iQlGiEaj8Pv9fS9UEAS4 PR5MT1/Hm9fr4Gq1WjuYzxfg0YOVSsUymcYoJxAMsr2iKMYr5Dc3maNalaGqKpTagaXA0JCKr a3vbX65XAZHVUqlEnPQ41BodbXvMUx+oVAwOtA0jTlcLhd4nm8TqTg1WskEvSOT3/6R9vb2MT x8ghEnJy9hdGzcsrL08wcWFx8jEomgWCwaw8TzKySX+wpJkkCm5vHibhB/6i0WbDSM9UBrsvX MyeNYWP6Md8/n4PP52Ksdmsb5Vx/J/8xG50T+FWAAPKDZ6z1/DUMAAAAASUVORK5CYII=">
a bude vypadat takto: (pokud zde nic nevidíte, použijte prohlížeč ne starší tří let)
Jak na DataURL?
Z příkladu je bystřejším patrné, jak DataURI vypadá. Jeho formát je: data[;charset=<encoding>][;base64],<data>. Tedy:
- Začíná názvem schématu
data
a dvojtečkou, - za ním je (nepovinný) parametr
MIME typ
, v němž uvedeme typ souboru (uvádějte jej a nespoléhejte na to, že to prohlížeč „nějak zpracuje“) - za ním je „znaková sada“ – v případě že jde o textové soubory, třeba text/html, jinak nemá smysl a lze ji vynechat
- za znakovou sadou je použité kódování – téměř výhradně se používá Base64. Parametr je sice opět nepovinný, ale je lépe ho uvést, nic tím nezkazíme
- za čárkou následují data v daném kódování (tedy převážně Base64)
Pokud budeme chtít takové DataURL vytvořit třeba pro miniobrázek červené tečky, budeme postupovat jednoduše: PNG soubor si převedeme do reprezentace v Base64 (
iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
) a zapíšeme URL jako:
9TXL0Y4OHwAAAABJRU5ErkJggg==
data:image/png;base64,(...a sem přijdou data...)
S generováním vám mohou pomoci nástroje jako The DataURLMaker (pro obrázky), the data: URI kitchen nebo elegantní Drag-n-drop DataURL generator. Můžete využít serverového skriptovacího jazyka, jak ukazuje např. výše odkázaný článek na Wikipedii:
<?php function data_uri($file, $mime) { $contents = file_get_contents($file); $base64 = base64_encode($contents); return ("data:$mime;base64,$base64"); } ?>
Kde to lze použít?
Použití pro obrázky je jasné – v atributu SRC u elementu IMG. DataURL lze použít i v CSS tam, kde se vyskytuje konstrukce url()
– u background-image, list-style-image atd. Můžete dataURL použít i v JavaScriptu, např. jako parametr pro window.open(). Můžete je použít jako favicon – zkrátka všude tam, kde používáte „běžná“ URL s http: či https:. Tedy např. i v <a href…> Můžete si zkusit drobnou ukázku DataURL.
Libůstky prohlížečů
IE ve verzi 8 vyžadoval, aby DataURL měl méně než 32kB a omezoval jeho použití na elementy img, link, object a input type=image. Od verze 9 tato omezení nejsou. Firefox zase nesnese znak konce řádku v datech (pozor na to, některé převaděče do Base64 zalamují řádky).
Co tím získáme? Co ztratíme?
Jako všude jsou zde jak výhody, tak nevýhody. Výhody jsou jasné – u malých souborů ušetříme velké množství přenesených dat, a to i přesto, že jsou DataURL o třetinu větší než původní soubory (viz princip kódování Base64). Navíc při použití transparentního gzip pro webové stránky je nárůst velikosti jen v řádu jednotek procent. Ušetříme si režii s otvíráním spojení (a co teprv u stránek přes HTTPS!) a vyhneme se frontám. DataURL můžeme použít např. i v CSS – pro nejrůznější obrázky na pozadí či ikony.
Výhodné je použití v případech, kde chcete mít jeden HTML soubor, bez doplňkových souborů, a přesto v něm použít ikony. Hezký příklad jsou „exportované záložky“ z prohlížeče Chrome – favicony jednotlivých webů jsou zapsány v HTML právě v podobě DataURL (Google vůbec DataURL poměrně intenzivně používá).
Na druhé straně jsou nevýhody: Takový obrázek se necachuje. U přímého odkazu může prohlížeč uložit soubor do cache a může ho použít třeba na jiné stránce. U DataURL žádný soubor není, cache se nekoná a v každé stránce bude muset být obsah znovu uvedený. Budou se načítat při každém refreshi (pokud je ale použijete v externím CSS souboru, tak to až tak neplatí). Při změně ikonek musíte změnit všechny DataURL ve všech souborech, kde jsou použitá… Někomu se nebude líbit „nehezký HTML kód“, zanesený „ošklivým nepohledným chaosem znaků“.
Používat, nebo nepoužívat?
Záleží na každém kodérovi či vývojáři a na jeho zvážení.
Podpora v prohlížečích je velmi slušná – viz Caniuse#dataURI.
Pro malé ikonky můžou výhody převážit. Posílat takto stokilovou fotografii se ale zcela jistě nevyplatí, tam nebudou úspory žádné. Nejdůležitější při rozhodování bude, jako vždy, pravidlo uměřenosti a vhodnosti pro daný cíl – bezhlavé nasazení DataURL všude možně bude mít jediný předpokládatelný výsledek: vlnu znechucení a odporu k „téhleté nové pitomosti“. Což by byla škoda.
skoda ze tam neni moznost zadat jmeno souboru, kdyz chci obrazek ulozit tak prohlizece nabizej ne moc idealni jmeno
Přidám tip pro programátory v Nette (nebo Latte). Datové URI můžete vytvořit v šablonách pomocí helperu dataStream, například:
<img src="{$img|dataStream}">
. MIME typ se bude detekovat automaticky. Podrobněji pak v dokumentaci.Cachuje to, nebo to při každém generování sahá po souboru na disku?
Přidám tip pro programátory v Ruby on Rails. Datové URI automaticky vytvoří správce css a js Jammit.
Přidám tip pro programátory v Javě. Převedení linkovaných obrázků v css na datové URI zajistí nástroj CSSEmbed.
Přidám tip pro čtenáře diskusí na zdrojáku. Automatické vytváření reklamních sdělení pana Grudla ustane až po odstranění viru Nette ze serverů root.cz.
Spam nesnáším a v Nette nedělám, ale ten jeho komentář mi nevadí – rád jsem se dozvěděl, že to tam jde takhle jednoduše zapsat. Stejně tak vítám ty tvoje odkazy na Jammit a CSSEmbed – ale příště by to snad šlo bez navážení se do ostatních, ne? :-)
Ach, zapomněl jsem, příště to jako každý slušný člověk napíšu anonymně, aby český blbeček neměl trauma.
Ale nenapíšeš a zase se několikanásobně ztrapníš.
Díky za odkaz na dokumentaci, je vážně skvělá! http://doc.nette.org/cs/quickstart
Díky, taky je za tím spousta práce.
A právě to, že se obrázek pravděpodobně bude přenášet více-krát (než kdyby byl kešovatelný), je dobrý důvod prohnat ho PNGCrushem a podobnými očišťovátky.
Zajímavý tip. Nejsem si jist, jak moc pomůžou „očišťovátka“ na soubory s velikostí v řádu jednotek kilobytů. Máš s tím nějakou zkušenost?
Záleží na tom, v čem a jak je PNG vytvořený. Pokud někdo vytvoří PNG třeba v Photoshop a uloží ho i s několika kilovým barevným profilem, tak odstraněním takového chunku se dosáhne výrazného zmenšení velikosti obrázků.
Další málo známá věc je, že u malých PNG obrázků, jako ikonky a odrážky, je nejvýhodnější používat 24 bitovou barevnou hloubku a pokud je nutná průhlednost, nebo průsvitnost, pak 32 bitovou barevnou hloubku. Je tomu tak proto, že obrázek neobsahuje barevnou paletu, která jinak zvětšuje velikost souboru.
Zdroj: http://www.root.cz/clanky/png-bity-byty-chunky/
Tohle jsem si neuvědomil, to je dobrá poznámka. Taknějak předpokládám, že vyhození barevných profilů při exportu obrázků pro web je samozřejmostí pro každého zkušenějšího grafika, ale přesto je tato poznámka rozhodně na místě.
Vaše druhé tvrzení, IMHO, není obecně platné (záleží na počtu barev). Z praktického hlediska bych jej však označil spíše přímo za mylné či nepravdivé.
Ikonky a odrážky mají typicky malý počet barev, čili barevná paleta pro ně je rovněž malá. Například ikonka tiskárničky z příkladu pro tento článek má v 24b hloubce 629 B, po konverzi do palety (30 barev) jsem na 347 B. To je úspora téměř 50%.
Vaše tvrzení by mělo naopak větší šanci na pravdivost například u fotek, tam se ale png používá velmi zřídka.
Ikonka tiskárny, použitá v článku, už je poměrně komplexní obrázek, takže máte pravdu, tvrzení není obecně platné. Sice jsem se při svých pokusech s tímto obrázkem dostal na jiné, menší hodnoty, 592 B a 328 B, ale to je více méně kosmetická změna.
Pro srovnání jsem zkusil červenou tečku, opět ze článku. Nejlépe, podle mého očekávání, vyšel dvoubarevný GIF, 47 B jedna barva nastavená na červenou, druhá barva libovolná, nastavená na průhlednou. Plnobarevná tečka s alfakanálem měla 85 B a tečka s paletou 102 B.
Nakonec jsem si vzal na paškál zdejší ikonku „Zobrazit vše“, http://i.iinfo.cz/z/ico-link-all-opinions.png k mému překvapení obsahuje ICC profil, velikost 3 102 B. Soubor s paletou má 430 B, plnobarevný soubor má nejméně, 404 B.
Pokud to někdo bude zkoušet, je možné, že dostane mírně odlišné výsledky, já jsem na pokusy využil IrfanView.
ono co sem si hral s png tak mnohdy icony apodobne veci mi stacilo 8bit png s pruhlednosti .. a vysledek byl mnohem mensi nez 32bit png … mimocodem pruhlednost de v png u kazde barevne hloubky .. a to jak alfa tak index …
odhliadnuc od kompresie, u 8 bitových obrázkov je veľkosť palety 768 Byteov ale jeden pixel predstavuje jedne Byte, u 24 bitobých obrázkov je jednen pixel 3 Bytes (RGB) takže bez kompresie by malo byť efektívnejšie použitie 24 bitov u obrázkov menších ako cca 16 x 16 pixelov.
Jenže komprese použitá v PNG formátu je docela účinná, zjednodušeně řečeno, čím jednodušší obrázek, tím účinnější komprese. V extrémném případě může i obrázek 500px × 500px vyjít lépe, jako plnobarevný, než s paletou.
http://teststranek.kvalitne.cz/images/99ccff-ff6666.png – je to barevný přechod, má 204 barev, exportovaný z Inkscape a prohnaný přes IrfanView, plugin PNGOUT.
Sám jsem byl překvapen, že tomu tak bylo u takto velkého obrázku.
> jak moc pomůžou „očišťovátka“ na soubory s velikostí v řádu jednotek kilobytů?
Stejně jako na soubory v desítkách KB – někdy v jednotkách procent, někdy v desítkách procent.
Ahoj,
jenom dodám, že tím lze vytvářet i HTML, máte-li Chrome, Operu či FF, zkuste si vložit do adresního řádku například
data:text/html,<input type=“number“ />
snad je zřejmé, jaký by měl být výsledek.
Použití možné pro velmi jednoduché demonstrace, sám to používám někdy pro zadávání bugů pro Chromium.
Ono to v článku je řečeno, ale ne přímo. Takže díky za komentář a dodávám, že nejen HTML, ale i JS a další – v podstatě cokoli, co má smysl.
Nemohu se ubránit pocitu, že si autor s přenosovou režií trochu zapřeháněl. Pro ikonu se nejspíš nebude navazovat nové TCP spojení, ale využije se již existující. Komunikace se tedy scvrkne na dva pakety – HTTP dotaz a odpověď. IP hlavička má 20 B, TCP hlavička také tak, HTTP hlavičky budou delší, takže to dohromady asi budou avizované „stovky bajtů“, ale těch stovek rozhodně nebude moc. Pokud klient stahuje ikonu více než jednou (tedy pokud člověk očekává, že uživatel na jeho webu navštíví víc než jednu stránku), vyplatí se normální URL – díky cache.
Kdysi jsem si dělal průzkum jak velká je průměrná HTTP hlavička. Jestli si dobře pamatuji tak mi vyšlo číslo někde mezi 400 a 500 bytů. Takže datové url se vám vyplatí pokud bude platit následující rovnice:
n*s*3 <= s + 400
Kde „n“ je pocet vyskytu toho datoveho elementu na strance. A „s“ je velikost elementu v bytech (jeste pred uencodovanim).
Upravou mi vychazi:
s(3n-1) <= 400
A pro n=1 mi vychazi ze velikost elementu musi byt mensi nez 200 bytů. Pro n=2 to uz musi byt mensi nez 80 bytu. Pro n=3 uz to je 50 bytu.
Nie je mi jasné, prečo má byť vo vzorci konštanta 3, keď dáta v base64 nie sú 3* väčšie, ale asi len o 37 percent, preto by som tam dal 1,37. Potom ten výsledok bude dosť iný, pre n=1 1333, n=2 307 a n=3 173.
Aha, v článku bylo napsáno o třetinu větší. A já četl třikrát větší a už si to nezkontroloval. Máte pravdu.
S drobnou korekturou (místo 3 je ta hodnota 1,3) mi vychází S=1333B pro N=1, 250 pro N=2 atd. Plus berme v úvahu, že u konverze PNG=>base64=>gzip bude výsledek ne o 33% větší, ale třeba jen o 2-3%…
„Šestkrát použitá stejná malá ikonka“ je vážně nesmysl. Rozumné to bude třeba ve chvíli, kdy půjde o odrážku v seznamu a bude v CSS jednou, když půjde o ikonku pro tisk atd.
Děsivé zbytečně létající kilobajty se prostě nekonají. Opravit, nepitvat.
Příklad z této stránky
Dotaz:
Odpověď:
Obrázek 619b, 1052b hlavičky, poměr payload/režie je 100/169. Obrázků s velikostí pod 700b je tu deset. Neplkat, spočítat.
adalsia vec – 400 musi byt sucet velkosti hlaviciek request+response, pretoze su obsiahnute v oboch
Ne 400 je jenom velikost hlavicky. Pro velikost request +response jsem pouzil „s+400“ v te prvni rovnici. V druhe to neni protoze „s“ jsem prevedl na levou stranu.
Nejde jen o pocet usetrenych Byte. Setri to i RAM na serveru v pripade, ze hojnou navstevnost (5000unikatnich za hodinu) – tedy optimalizuje vykon.
BTW ve starsich IE ktery DataUrl nepodporujou se da pouzit MHTML.
http://www.phpied.com/mhtml-when-you-need-data-uris-in-ie7-and-under/
Ale zas je nevyhoda ze se to musi generovat dvakrat (jednou MHTML a jednou DataUrl).
Pěkný článek, díky. Jen nějak nechápu, proč je zařazen do seriálu o HTML5 – vždyť to RFC 2397 je z roku 1998 a tahle URL jdou použít kdekoli.
Ad „budeme postupovat jednoduše: PNG soubor si převedeme do reprezentace v Base64“
Jednodušší mi tedy přijde použít příkaz
base64
, jak jsem kdysi psal. IMHO je zbytečné pro takové banální úkoly používat nějaké webové služby a skriptování na straně serveru, když to jde lokálně, v rámci jednoho počítače a disku, pomocí jednoho příkazu.Ad „Ušetříme si režii s otvíráním spojení“
Naštěstí se dneska většinou používá
Connection: Keep-Alive
, takže TCP spojení se nenavazuje pro každý obrázek zvlášť. A data navíc jsou pak „jen“ ty HTTP hlavičky.Když už člověk musí takhle optimalizovat, tak mi přijde rozumnější poskládat všechny malé obrázky do jednoho většího a pak z něj pomocí CSS zobrazovat vždy tu jednu malou část. Takhle to dělá třeba Google – viz
Ad „Výhodné je použití v případech, kde chcete mít jeden HTML soubor“
Tohle už je zajímavější využití.
Ad „Někomu se nebude líbit ‚nehezký HTML kód‘, zanesený ‚ošklivým nepohledným chaosem znaků‘.“
Podle mého zbytečná starost – ten kód je zpracováván strojem (prohlížečem) a ne člověkem, takže je to jedno, na nějaké kráse tu nezáleží. U výstupního (X)HTML neřeším ani odsazení řádků. (skript nebo program, který tu stránku generuje, je samozřejmě věc jiná)
Máš recht, udělám extra seriál na věci, co jsou míň známé, ale nespadají do HTML5, takový „Webařův průvodce po temných zákoutích“.
Použít „příkaz base64“ je taky možné – pokud v systému je. Určitě by šlo tuhle konverzi naHOOKovat třeba do verzovacího systému…
Já jsem to používal ve spojení s lokální databází pro webovou aplikaci (resp. proprietální framework na jejich tvorbu) pro iOs Safari – obrázky byly stahovány base64 zakódované a uloženy do databáze, a javascript je pak dynamicky vkládal do stránek k img elementům podle hodnoty jejich name atributu.
Nevím jestli to byl (a stále je) úplně nejlepší způsob (a ani mi o to teď nejde, jde mi o příklad práce s data url), ale fungoval spolehlivě, zatímco např. použití cache manifest pro offline režim bylo tehdy pro data stahovaná ajaxem prostě nefunkční.
Za pripomenuti stoji, ze element canvas podporuje funkci toDataUrl().
Zkuste:
window.open(canvas.toDataURL(‚image/png‘));
Zrovna pro ikonky se mi docela líbí postup, kdy si je všechny umístíte na jeded obrázek a potom zobrazujete z daného obrázku jen příslušnou část (pomocí CSS, tedy clipingu nebo umístění pozadí). Chce si to s tím trochu pohrát, ale máte potom zadarmo jen samé výhody. Obrázek je nakešován a žádná další spojení se neotevírají. Docela by bodlo, kdyby browser uměl pracovat s archivy (tedy URL by bylo na archiv, jeden soubor, jedno spojení a browser by si z archivu vzal příslušnou část). Něco jako src=’http://mojedomena.cz/images.zip{left.png}‘.
obrázek se zobrazí, ale odkaz nefunguje
jinak bych řekl, že se to zatím moc neujalo z toho důvodu, že na první stránce webu se sice stahuje hodně různých souborů (nejen obrázků), ale ty pak zůstanou v cache a další stránky už tento „balast“ nestahují, ale používají z lokální cache PC
zatímco ikona v datové url se musí stahovat pokaždé
Když bude v externím CSS souboru, tak se taky stáhne jen jednou. Pak je otázka, jestli se chceš patlat s pozicováním/ořezáváním jednoho velkého obrázku, nebo se smířit s tím trochu větším objemem dat způsobeným base64 kódováním. A pak je taky možnost se na obě tyhle optimalizace úplně vykašlat, což bude mnohdy nejlepší volba.
Právě kvůli budoucímu redesignu či pouhé výměně několika ikonek je a bude ideálnější sprite. (http://spriteme.org/)
Použití DataURL bych viděl třeba v zmiňované faviconě.
ano, sprity celý problém se separátními http requesty a cachováním řeší podstatně lépe, podstatný problém je ale relativně složitá implementace a rozvržení ikonek na sprit, repeat-x a repeat-x pozadí vyžadují separátní soubor.
Clanek ocenuji, ale mohl jit vic do hloubky a neomezovat se jen na usporu datoveho toku. Kvuli tomu to snad ani nebylo navrzene.
Datova URL se hodi proste tehdy, kdyz je potreba programove generovat obrazky, at uz na strane serveru nebo browseru (JavaScript). Obrazky v textove podobe se snadno prenaseji pomoci cisteho JSONu, [JSON|XML]-RPC, GET parametry, POST data, canvas.toDataUrl(), apod…
Napadu je hodne. A neni treba se omezovat jen na ikony.
Když už to tu má Nette a Ruby tak aby si Django nepřipadalo smutně tak jsem zbastlil menší filter: https://gist.github.com/1153019