0. Drobné odbočení na úvod k atributu download
Jistě jste ve své praxi řešili případ, kdy bylo na stránce potřeba odkázat na soubor s tím, že se po kliknutí uživateli neotevře, ale nabídne mu uložení jako download. U souborů s „dostatečně exotickým “ MIME typem (míněno exotickým pro prohlížeč) to tak funguje vždy. Odkaz <a href="blabla.zip">
vyvolá rovnou stahovací dialog a nepokouší se nic otvírat.
Ale co obrázky, skripty nebo HTML stránky? Jak donutit prohlížeč, aby je neotvíral, ale nabízel ke stažení? Jedna z možností je nastavit na serveru patřičné hlavičky ( Content-Disposition
atd.). Druhá možnost je o něco jednodušší: K odkazu se napíše poznámka „Klikněte pravým tlačítkem a vyberte Uložit odkaz jako…“
Tento problém řeší navrhovaný atribut download. Jeho hodnotou je jméno, pod nímž by měl být soubor uložen (samosebou si jej může uživatel v dialogu změnit).
<a href="logo.png" download="Logo Zdroják.cz">Stáhněte si naše logo</a>
Výsledkem je, že prohlížeč po kliknutí rovnou otevře ukládací dialog a nabídne uložení souboru. Šikovná věc, ale na jásání je brzy – atribut download je v tuto chvíli implementován pouze v Chrome dev verzi (14.0.835.15+).
Zajímavé možnosti nabídne tento atribut ve spojení s blobURI či filesystem URI – a o nich bude řeč právě v souvislosti s FileAPIs.
1. Souborová API
Specifikace File API, File Writer API a File System API, resp. jejich návrhy, tvoří základ veškeré práce se soubory v prohlížečích. Pro všechny platí, že jsou v prohlížečích v různém stádiu implementace a zatím se nelze stoprocentně spolehnout, že v konkrétním prohlížeči budou fungovat, resp. že budou fungovat všechny funkce. Proto minimálně v následujících měsících využijete nejrůznější polyfills, tedy knihovny, které detekují existenci technologií, a pokud nejsou implementovány, nabídnou vlastní implementaci.
1. 1. File API
Toto API definuje základní konstrukce pro práci se soubory – Blob a File, metody pro čtení dat, synchronní čtení dat, vytváření URI pro soubory, chyby, výjimky a události, které se k souborům vážou.
1. 2. File Writer API
Druhé API přináší možnost vytvářet soubory v prohlížeči a zapisovat je lege artis na disk. Definuje BlobBuilder, základní nástroj pro vytváření souborů, a nástroje FileSaver a FileWriter, které ukládají obsah blobu na disk. Specifikace je stále ve fázi návrhu, takže je možné, že FileWriter se stane součástí Filesystem API.
1. 3. File System API
Toto API slouží pro práci se „sandboxed filesystem“, tedy s prostorem na disku, vyhrazeným pro „bezpečnou práci se soubory z prostředí prohlížeče“. Skripty tedy nebudou moci číst a zapisovat, kam se jim zamane, a přepsat tak např. systémové soubory; jejich prostor je omezen. Více o tomto API naleznete v článku První krůčky s FileSystem API.
2. Blob
Blob, neboli Binary Large OBject, je pojem, známý např. ze SQL či programovacích jazyků, kde vyjadřuje nestrukturovaný soubor binárních dat. Nestrukturovaný je zde myšleno ve vztahu k aplikaci, která s ní pracuje; například binární reprezentace obrázku ve formátu PNG zcela jistě vnitřní strukturu má, ovšem pokud je takový obrázek poslán na server a tam uložen do databáze, aniž by s ním aplikace jakkoli pracovala, měnila ho nebo z něj četla informace, může na něj nahlížet jako na shluk binárních dat, které pro ni nemají bezprostřední význam.
Podle File API je Blob objekt, který obsahuje právě ta binární data a navenek dává k dispozici readonly atributy size
a type
a nabízí metodu slice(start, length, type)
. Atribut size udává velikost souboru v bajtech a type obsahuje MIME typ dat. Metoda slice slouží k vytvoření nového blobu ze stávajícího pomocí zkopírování části, definované počátečním bajtem (start) a délkou (length). Třetí atribut je nepovinný a slouží k případné změně typu blobu.
3. File
Potomkem „třídy“ Blob je File – k výše popsaným atributům přidává další R/O atributy name
a lastModifiedDate
. Name obsahuje jméno souboru (jen jméno bez cesty) a lastModifiedDate informaci o poslední změně souboru.
4. FileReader
Pokud chceme pracovat s daty v blobu či souboru přímo v JavaScriptu, musíme je nejprve přečíst – tedy převést do tvaru, který lze v JS zpracovávat. K tomu slouží právě metody objektu FileReader. FileReader nabízí základní metody readAsBinaryString
, readAsDataURL
, readAsText
a readAsArrayBuffer
.
var reader = new FileReader(); reader.onload = function(event) { ... }; reader.readAsBinaryString(blob);
V obsluze události onload můžeme zpracovat načtený obsah ve tvaru binárního řetězce (readAsBinaryString), datového URL (readAsDataURL), textu (readAsText) či ArrayBuffer. Obsah bude dostupný přes vlastnost result
:
reader.onload = function(event) { ... var data = event.target.result; ...}
Všechny zmíněné metody mají jako parametr blob. U metody readAsText() lze zadat i druhý nepovinný parametr encoding a určit případné překódování znaků.
FileReader nabízí i atributy readyState
a error
. První obsahuje stav readeru (0 = nic se nedělá, 1 = probíhá čtení, 2 = čtení dokončeno), druhý obsahuje objekt FileError
, který udává chybu, ke které při čtení došlo.
Kromě zmíněné události load
nabízí FileReader i události loadstart
, loadend
(voláno při začátku čtení, resp. při skončení), error
(při výskytu chyby), abort
(při skončení čtení) a progress
(voláno během čtení). U události progress můžeme z atributů loaded a total zjistit, kolik dat bylo zpracováno a kolik zbývá.
reader.onprogress = updateProgress; ... function updateProgress(event) { if (event.lengthComputable) { var loaded = (event.loaded / event.total); if (loaded < 1) { bar.style.width = (loaded * 200) + "px"; } } }
Pěkné… ale kde ten blob vezmu?
Velmi správná otázka. Hovoříme tu o zpracování souborů, ale zatím nepadlo ani slovo o tom, jak se z klasického souboru, co leží někde na disku, stane blob.
Máme několik způsobů (a teď pomineme File System API). Můžeme si blob ručně vytvořit pomocí BlobBuilderu (o kterém bude ještě řeč), nebo můžeme nechat uživatele, aby soubor vybral a v prohlížeči otevřel.
Můžeme využít buď novější postup s drag-and-drop (viz článek Práce se soubory v HTML5) nebo klasický způsob s <input type=file>
. Samozřejmě lze použít i obě metody najednou (což může přispět ke zlepšení použitelnosti) – samotná obsluha načítání souborů zůstává stejná, mění se pouze sledované události. U elementu input file můžeme obsloužit událost change
:
<input type=file id=upload>
var upload = document.getElementById('upload'); upload.onchange = function (event) { var file = upload.files[0]; // ve file máme kýžený "blob" pro další zpracování }
Pole files
u elementu input file obsahuje jednotlivé soubory jako položky typu File
. Pokud je použit v elementu atribut multiple
, může být položek víc, jinak bude obsahovat položku jedinou.
Blob lze vytvořit i interně, a to jak „na zelené louce“ (pomocí Builderu), tak z HTML elementů, které podporují metodu toBlob()
(např. canvas). Obojí bude námětem dalších dílů.
Příklad: Jak načíst soubor?
Ukažme si použití těchto informací v jednoduchém příkladu: necháme uživatele vybrat soubor s obrázkem, a ten zobrazíme. Autorem příkladu je Remy Sharp.
V HTML je několik prvků pro uživatelské rozhraní:
<p id="status">File API & FileReader API not supported</p> <p><input type=file></p> <p>Select an image from your machine to read the contents of the file without using a server</p> <div id="holder"></div>
Odstavec #status udává stav aplikace, tedy zda je čtení souborů podporováno a zda je možné je použít. Dále je v kódu vlastní input pro výběr souboru, popis a div#holder, v němž se bude obrázek zobrazovat.
Zbytek je obsloužen skriptem. Je testována dostupnost API:
if (typeof window.FileReader === 'undefined') { state.className = 'fail'; } else { state.className = 'success'; state.innerHTML = 'File API & FileReader available'; }
a následně je nadefinována obsluha:
var upload = document.getElementsByTagName('input')[0], holder = document.getElementById('holder'), state = document.getElementById('status'); upload.onchange = function (e) { e.preventDefault(); var file = upload.files[0], reader = new FileReader(); reader.onload = function (event) { var img = new Image(); img.src = event.target.result; // note: no onload required since we've got the dataurl...I think! :) if (img.width > 560) { // holder width img.width = 560; } holder.innerHTML = ''; holder.appendChild(img); }; reader.readAsDataURL(file); return false; };
V obsluze události change je nejprve zabráněno vykonání předdefinovaných akcí. Výše uvedeným způsobem je získán z vybraného souboru blob a vytvořen nový FileReader, kterému je blob předán jako parametr metody readAsDataURL()
. Obsluha události onload vytvoří HTML element <img>
, nastaví mu atribut src
na hodnotu, která je vrácena Readerem jako result
– tedy datové URL, a uloží jej do připraveného kontejneru holder
.
V příkladu nejsou ošetřeny chybové stavy ani není obrázek nějak upravován. Jde čistě jen o demonstraci čtení souborů. (Můžete se podívat na tentýž příklad s použitím drag-and-drop)
A dál…?
V dalším pokračování si probereme vytváření souborů v prohlížeči a jejich zápis. Podíváme se, jak uložit obrázek z canvasu a jak vytvořit dočasnou kódovou URL pro objekt.
Přehled komentářů