Práce se soubory v prohlížeči, díl 1

Už několikrát jsme v článcích na Zdrojáku použili FileAPI, relativně novou součást webových technologií, která umožňuje číst, zpracovávat a ukládat soubory. V této minisérii se podíváme na nejrůznější nástroje a rozhraní, která využijete při zpracovávání souborů v prohlížečích, trochu podrobněji.

Seriál: Webdesignérův průvodce po HTML5 (21 dílů)

  1. Webdesignérův průvodce po HTML5 – díl nultý 25.5.2010
  2. Webdesignérův průvodce po HTML5 – nová sémantika 1.6.2010
  3. Webdesignérův průvodce po HTML5 – nová sémantika II 8.6.2010
  4. Webdesignérův průvodce po HTML5 – pohyblivé obrázky 15.6.2010
  5. Webdesignérův průvodce po HTML5 – používáme pohyblivé obrázky 22.6.2010
  6. Webdesignérův průvodce po HTML5 – taháme data od návštěvníka 29.6.2010
  7. HTML5 Audio: rádio ve vašich stránkách 13.7.2010
  8. Webdesignérův průvodce po HTML5: Microdata 20.7.2010
  9. AppCache: webové aplikace i bez připojení 27.7.2010
  10. Webdesignérův průvodce po HTML5: WebStorage 3.8.2010
  11. Webdesignérův průvodce po HTML5: Multithreading s WebWorkers 10.8.2010
  12. Webdesignérův průvodce po HTML5: Databáze v prohlížečích 17.8.2010
  13. Webdesignérův průvodce po HTML5: Shrnutí a rozhrnutí 24.8.2010
  14. HTML5: ukládáme si data k elementům 6.12.2010
  15. Webdesignérův průvodce po HTML5: Táhni a srůstej 5.1.2011
  16. HTML5: První krůčky s FileSystem API 15.2.2011
  17. Mobilizujeme web v HTML5 4.4.2011
  18. Single Page Apps a řešení problémů s historií 1.6.2011
  19. Page Visibility API: Kouká na mě vůbec někdo? 10.8.2011
  20. Práce se soubory v prohlížeči, díl 1 15.8.2011
  21. Práce se soubory v prohlížeči, díl 2 5.9.2011

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, readAsTextreadAsArrayBuffer.

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 (readAsBinaryS­tring), 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 &amp; 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.

Začal programovat v roce 1984 s programovatelnou kalkulačkou. Pokračoval k BASICu, assembleru Z80, Forthu, Pascalu, Céčku, dalším assemblerům, před časem v PHP a teď by rád neprogramoval a radši se věnoval starým počítačům.

Komentáře: 16

Přehled komentářů

bauglir Poslední ukázka
Martin Malý Re: Poslední ukázka
Roman Jakubec Re: Poslední ukázka
bauglir Re: Poslední ukázka
besh input nebo drag'n'drop
Martin Malý Re: input nebo drag'n'drop
starenka OT ad 0: (vylev zhrzeneho genia)
Martin Malý Re: OT ad 0: (vylev zhrzeneho genia)
Jiří Kosek Re: OT ad 0: (vylev zhrzeneho genia)
František Kučera HTTP ne HTML
František Kučera Re: OT ad 0: (vylev zhrzeneho genia)
Martin Malý Re: OT ad 0: (vylev zhrzeneho genia)
František Kučera Re: OT ad 0: (vylev zhrzeneho genia)
Jiří Kosek Re: OT ad 0: (vylev zhrzeneho genia)
Jiří Kosek Re: OT ad 0: (vylev zhrzeneho genia)
misaz A výsledek?
Zdroj: https://www.zdrojak.cz/?p=3530