ArrayBuffer a náhledy jsou souÄásti standardu ECMA, souÄásti JavaScriptu.
V prohlÞeÄi jsou i dalšà objekty vyššà úrovnÄ, popsané ve specifikaci souborového API, konkrétnÄ Blob.
Blob se skládá z nepovinného ÅetÄzce type (zpravidla MIME typ) a z blobParts â posloupnost jiných objektů Blob, ÅetÄzců a objektů BufferSource.
Syntaxe konstruktoru je:
new Blob(blobParts, volby);
blobPartsje pole hodnotBlob/BufferSource/String.volbyje nepovinný objekt:typeâ typ blobu, zpravidla MIME typ, napÅ.image/png,endingsâ zda pÅevádÄt znaky konce Åádku vBlobtak, aby odpovÃdaly koncům Åádků v aktuálnÃm OS (\r\nnebo\n). StandardnÄ"transparent"(nedÄlá nic), ale může být i"native"(pÅevádÃ).
PÅÃklad:
// vytvoÅÃme Blob z ÅetÄzce
let blob = new Blob(["<html>â¦</html>"], {type: 'text/html'});
// vÅ¡imnÄte si: prvnà argument musà být pole [...]
// vytvoÅÃme Blob z typového pole a ÅetÄzců
let ahoj = new Uint8Array([65, 104, 111, 106]); // "Ahoj" v binárnÃm tvaru
let blob = new Blob([ahoj, ' ', 'svÄte'], {type: 'text/plain'});
Äásti blobu můžeme zÃskat pomocÃ:
blob.slice([poÄáteÄnÃByte], [koncovýByte], [typObsahu]);
poÄáteÄnÃByteâ poÄáteÄnà byte, standardnÄ 0.koncovýByteâ poslednà byte (nebude zahrnut, standardnÄ až do konce).typObsahuâtypenového blobu, standardnÄ stejný jako ve zdroji.
Argumenty se podobajà argumentům pole.slice, jsou povoleny i záporné hodnoty.
Blob jsou nemÄnnéV objektech Blob nemůžeme pÅÃmo mÄnit data, ale můžeme z nich extrahovat jejich Äásti, vytváÅet z nich nové objekty Blob, smÃchávat je do nového objektu Blob a podobnÄ.
Toto chovánà se podobá JavaScriptovým ÅetÄzcům: nemůžeme zmÄnit znak v ÅetÄzci, ale můžeme vytvoÅit nový, opravený ÅetÄzec.
Blob jako URL
Blob můžeme snadno použÃt jako URL pro <a>, <img> nebo jiné znaÄky, abychom zobrazili jeho obsah.
DÃky vlastnosti type můžeme také Blob objekty stahovat nebo je nahrávat jinam. Z jejich type se pak pÅirozenÄ stane Content-Type v sÃÅ¥ových požadavcÃch.
ZaÄneme jednoduchým pÅÃkladem. KliknutÃm na odkaz si stáhnete dynamicky generovaný Blob s obsahem Ahoj, svÄte! jako soubor:
<!-- atribut download donutà prohlÞeÄ soubor stáhnout a nepÅecházet na nÄj -->
<a download="hello.txt" href='#' id="odkaz">Stáhnout</a>
<script>
let blob = new Blob(["Ahoj, svÄte!"], {type: 'text/plain'});
odkaz.href = URL.createObjectURL(blob);
</script>
Můžeme také vytvoÅit odkaz dynamicky v JavaScriptu a simulovat kliknutà na nÄj volánÃm odkaz.click(). Pak se stahovánà automaticky spustÃ.
Následuje podobný kód, který pÅimÄje uživatele stáhnout dynamicky vytvoÅený Blob bez jakéhokoli HTML:
let odkaz = document.createElement('a');
odkaz.download = 'hello.txt';
let blob = new Blob(['Ahoj, svÄte!'], {type: 'text/plain'});
odkaz.href = URL.createObjectURL(blob);
odkaz.click();
URL.revokeObjectURL(odkaz.href);
URL.createObjectURL vezme Blob a vytvoÅà pro nÄj unikátnà URL ve tvaru blob:<původ>/<uuid>.
Hodnota odkaz.href vypadá následovnÄ:
blob:https://javascript.info/1e67e00e-860d-40a5-89ae-6ab0cbee6273
Pro každé URL generované volánÃm URL.createObjectURL si prohlÞeÄ vnitÅnÄ uložà mapovánà URL â Blob. Taková URL jsou tedy krátká, ale umožÅujà pÅÃstup k blobu.
Vygenerované URL (a tedy i odkaz s nÃm) je platné jedinÄ uvnitÅ aktuálnÃho dokumentu, dokud je otevÅený. A umožÅuje odkazovat se na Blob v <img>, <a>, v zásadÄ v kterémkoli jiném objektu, který oÄekává URL.
Má to vÅ¡ak vedlejšà efekt. Dokud existuje mapovánà pro Blob, samotný Blob pÅetrvává v pamÄti. ProhlÞeÄ jej nemůže uvolnit.
Když je dokument zavÅen, mapovánà se automaticky odstranÃ, takže objekty Blob jsou poté uvolnÄny. Jestliže vÅ¡ak aplikace bÄžà dlouhou dobu, nestane se to hned tak brzy.
Když tedy vytvoÅÃme URL, tento Blob zůstane viset v pamÄti, i když už nenà zapotÅebÃ.
URL.revokeObjectURL(url) odstranà odkaz z vnitÅnÃho mapovánÃ, ÄÃmž umožnÃ, aby byl Blob smazán (pokud na nÄj nenà žádný jiný odkaz) a pamÄÅ¥ uvolnÄna.
V poslednÃm uvedeném pÅÃkladu jsme zamýšleli použÃt Blob pouze jednou, pro okamžité staženÃ, proto okamžitÄ voláme URL.revokeObjectURL(odkaz.href).
V pÅedchozÃm pÅÃkladu s HTML odkazem, na který lze kliknout, vÅ¡ak URL.revokeObjectURL(odkaz.href) nevoláme, protože tÃm bychom URL blobu zneplatnili. Po jeho zruÅ¡enà a odstranÄnà mapovánà již URL nefunguje.
Blob na base64
Alternativou k URL.createObjectURL je pÅevedenà objektu Blob na ÅetÄzec zakódovaný do base64.
Toto kódovánà reprezentuje binárnà data jako ÅetÄzec ultrabezpeÄných âÄitelnýchâ znaků s ASCII kódy od 0 do 64. A co je jeÅ¡tÄ důležitÄjÅ¡Ã, toto kódovánà můžeme použÃvat v âdatových URLâ.
Datové URL má tvar data:[<mediatype>][;base64],<data>. Taková URL můžeme použÃvat vÅ¡ude, kde můžeme použÃvat âbÄžnáâ URL.
NapÅÃklad zde je smajlÃk:
<img src="data:image/png;base64,R0lGODlhDAAMAKIFAF5LAP/zxAAAANyuAP/gaP///wAAAAAAACH5BAEAAAUALAAAAAAMAAwAAAMlWLPcGjDKFYi9lxKBOaGcF35DhWHamZUW0K4mAbiwWtuf0uxFAgA7">
ProhlÞeÄ dekóduje ÅetÄzec a zobrazà obrázek:
K pÅevedenà objektu Blob na base64 použijeme zabudovaný objekt FileReader, který může naÄÃtat data z blobů v mnoha formátech. PodrobnÄji to probereme v pÅÃÅ¡tà kapitole.
Následuje demo stahovánà blobu, nynà pomocà base64:
let odkaz = document.createElement('a');
odkaz.download = 'hello.txt';
let blob = new Blob(['Ahoj, svÄte!'], {type: 'text/plain'});
let reader = new FileReader();
reader.readAsDataURL(blob); // pÅevede blob na base64 a volá onload
reader.onload = function() {
odkaz.href = reader.result; // datové URL
odkaz.click();
};
Oba způsoby vytvoÅenà URL pro Blob jsou použitelné, ale URL.createObjectURL(blob) je obvykle jednoduššà a rychlejÅ¡Ã.
- Pokud se staráme o pamÄÅ¥, musÃme je odstranit.
- PÅÃmý pÅÃstup do blobu bez âkódovánÃ/dekódovánÃâ.
- Nenà tÅeba nic odstraÅovat.
- Na velkých objektech
Blobdocházà kvůli kódovánà ke spotÅebÄ výkonu a pamÄti.
PÅevod obrázku na blob
Můžeme vytvoÅit Blob z obrázku, Äásti obrázku nebo dokonce můžeme vytvoÅit screenshot stránky. To se hodÃ, když jej chceme nÄkam nahrát.
Operace s obrázky provádÃme pomocà elementu <canvas>:
- NakreslÃme obrázek (nebo jeho Äást) na plátno volánÃm canvas.drawImage.
- Voláme metodu plátna .toBlob(callback, format, quality), která vytvoÅÃ
Blob, a až bude hotový, spustà na nÄmcallback.
V následujÃcÃm pÅÃkladu je obrázek jen zkopÃrován, ale pÅed vytvoÅenÃm blobu můžeme také vyjmout jeho Äást nebo jej na plátnÄ nÄjak transformovat:
// vezmeme libovolný obrázek
let obrázek = document.querySelector('img');
// vytvoÅÃme <canvas> stejné velikosti
let plátno = document.createElement('canvas');
plátno.width = obrázek.clientWidth;
plátno.height = obrázek.clientHeight;
let kontext = plátno.getContext('2d');
// zkopÃrujeme do nÄj obrázek (tato metoda umožÅuje vyjmout jeho Äást)
kontext.drawImage(obrázek, 0, 0);
// na plátnÄ můžeme volat kontext.rotate() a provádÄt mnoho dalÅ¡Ãch vÄcÃ
// toBlob je asynchronnà operace, po jejÃm dokonÄenà se volá callback
plátno.toBlob(function(blob) {
// blob je pÅipraven, stáhneme ho
let odkaz = document.createElement('a');
odkaz.download = 'example.png';
odkaz.href = URL.createObjectURL(blob);
odkaz.click();
// smažeme vnitÅnà odkaz na blob, aby ho prohlÞeÄ mohl uvolnit z pamÄti
URL.revokeObjectURL(odkaz.href);
}, 'image/png');
Pokud pÅed callbacky dáváme pÅednost async/await:
let blob = await new Promise(splÅ => plátno.toBlob(splÅ, 'image/png'));
Pro vytvoÅenà screenshotu stránky můžeme použÃt knihovnu jako https://github.com/niklasvh/html2canvas, která provádà to, že prostÄ jen projde stránku a vykreslà ji na <canvas>. Pak můžeme zÃskat jejà Blob stejným způsobem jako výše.
Od Blobu k ArrayBufferu
Konstruktor Blob umožÅuje vytvoÅit blob témÄÅ z Äehokoli, vÄetnÄ jakéhokoli BufferSource.
Pokud vÅ¡ak potÅebujeme provádÄt zpracovánà na nižšà úrovni, můžeme zÃskat ArrayBuffer nejnižšà úrovnÄ volánÃm blob.arrayBuffer():
// zÃskáme arrayBuffer z blobu
const pÅÃslibBufferu = await blob.arrayBuffer();
// nebo
blob.arrayBuffer().then(buffer => /* zpracovánà ArrayBufferu */);
Od Blobu k proudu
Když naÄÃtáme a zapisujeme do blobu vÃce než 2 GB dat, bude pro nás použÃvánà arrayBuffer pamÄÅ¥ovÄ nároÄnÄjÅ¡Ã. V této chvÃli můžeme pÅevést blob pÅÃmo na proud.
Proud (stream) je speciálnà objekt, ze kterého můžeme ÄÃst (nebo do nÄj zapisovat) po Äástech. Zde je to mimo náš rámec, ale bude následovat pÅÃklad a vÃce se o tom můžete doÄÃst v https://developer.mozilla.org/en-US/docs/Web/API/Streams_API. Proudy se hodà pro data, která je vhodné zpracovávat po jednotlivých Äástech.
Metoda stream() rozhranà Blob vracà proud ReadableStream, který pÅi Ätenà vracà data obsažená v tomto blobu.
Pak z nÄj můžeme ÄÃst následovnÄ:
// zÃskáme readableStream z blobu
const readableStream = blob.stream();
const stream = readableStream.getReader();
while (true) {
// pro každou iteraci: value (hodnota) je dalšà fragment blobu
let { done, value } = await stream.read();
if (done) {
// v proudu již nejsou dalšà data
console.log('celý blob zpracován.');
break;
}
// provedeme nÄco s Äástà dat, kterou jsme právÄ naÄetli z blobu
console.log(value);
}
ShrnutÃ
ZatÃmco ArrayBuffer, Uint8Array a jiné objekty BufferSource jsou âbinárnà dataâ, Blob reprezentuje âbinárnà data spolu s typemâ.
DÃky tomu jsou bloby vhodné pro operace stahovánà a nahrávánÃ, které se v prohlÞeÄi použÃvajà velice Äasto.
Metody, které provádÄjà webové požadavky, napÅ. XMLHttpRequest, fetch a tak dále, mohou s Blob pÅirozenÄ pracovat stejnÄ jako s jinými binárnÃmi typy.
Mezi Blob a binárnÃmi datovými typy nižšà úrovnÄ můžeme snadno pÅevádÄt:
- Můžeme vytvoÅit
Blobz typového pole konstruktoremnew Blob(...). - Z blobu můžeme zÃskat zpÄt
ArrayBuffervolánÃmblob.arrayBuffer()a pak na nÄm vytvoÅit náhled pro binárnà zpracovánà na nižšà úrovni.
Když potÅebujeme pracovat s velkým blobem, jsou velmi užiteÄné konverznà proudy. Z blobu můžeme snadno vytvoÅit ReadableStream. Metoda stream() rozhranà Blob vrátà ReadableStream, který pÅi Ätenà vracà data obsažená v blobu.
KomentáÅe
<code>, pro nÄkolik Åádků je obalte znaÄkou<pre>, pro vÃce než 10 Åádků vložte odkaz na pÃskoviÅ¡tÄ (plnkr, jsbin, codepenâ¦)